Функции arduino void loop () и void setup ()

Объяснение скетча для мастера

Основная часть кода как для ведущего, так и для ведомых устройств — это то, что я называю логическим кодом мигания. Чтобы мигнуть светодиодом 13 на Ардуино, мы должны сделать следующее:

  • Добавим глобальные переменные , , и в верхней части нашего скетча
  • Инициализируйте значения глобальных переменных внутри функции
  • Инициализируйте контакт 13 Arduino как выходной контакт внутри с помощью
  • Добавим код логики мигания внутри функции

Библиотека Wire

Для использования встроенного интерфейса I2C Arduino мы будем использовать библиотеку Wire.

Эта библиотека поставляется в стандартной комплектации с Arduino IDE. Как и в других библиотеках Arduino, библиотека Wire имеет готовые I2C функции, чтобы сделать кодирование проще для нас.

Чтобы использовать функции библиотеки Wire, мы должны добавить его сначала в наш эскиз. В эскизе выше, у нас есть следующая строка в верхней части:

После включения библиотеки мы можем использовать встроенные функции библиотеки.

Первое, что нужно сделать, это подключить устройство к шине I2C. Синтаксис для этого — . Адрес является необязательным для мастер-устройств. Итак, для эскиза мастера Arduino, мы просто добавляем код внутри .

Теперь мы переходим к циклу . Наш код заставит Arduino прочитать значение потенциометра, подключенного к контакту A0, и сохранить его в переменной .

Отправка данных

После сохранения значения с пина A0 в переменную , мы можем отправить значение по I2C. Отправка данных по I2C включает в себя три функции:

Wire.beginTransmission()

Мы инициируем команду отправки, сначала информируя устройства на шине о том, что мы будем отправлять данные.

Для этого мы вызываем функцию . Адрес — это I2C-адрес ведомого прибора, который будет принимать данные. Эта функция делает две вещи:

  1. Она информирует шину о том, что мы будем посылать данные.
  2. Он информирует предполагаемого получателя о том, что данные готовы к получению.

Wire.endTransmission()

После отправки данных нам необходимо освободить сеть, чтобы позволить другим устройствам общаться по сети. Это делается с помощью функции .

Наше ведущее устройство также должно получить положение потенциометра от ведомого устройства. Мы делаем это с помощью , и .

Wire.requestFrom()

Полным синтаксисом запроса данных от ведомого устройства является Wire.requestFrom(адрес, количество).

Адрес — это I2C-адрес ведомого устройства, от которого мы должны получить данные, а количество — это количество байтов, которое нам нужно. Для нашего проекта, адрес ведомого устройства 0x08 и нам нужен один байт.

Внутри мы используем для запроса одного байта данных от ведомого устройства 0x08.

После выдачи команды , за ней должна следовать команда чтения для получения ответа от шины I2C.

Write.available()

Сначала мы проверяем, есть ли данные на шине. Это делается с помощью функции внутри условного оператора . Функция возвращает количество байт, ожидающих чтения.

Wire.read();

Для получения доступных данных мы используем функцию и сохраняем возвращаемое значение в переменную . Каждый вызов функции получает только один байт данных из шины I2C.

Диапазон переменных

Arduino IDE базируется на языке C/C++. Из него же позаимствован способ обработки переменных. При написании программы можно использовать как глобальные переменные, так и локальные переменные.

Глобальная переменная — это такая переменная, который инициализируется при запуске программы и доступна из любого места (функции) в течение всего времени действия программы.

В основном это является преимуществом, поскольку из любой функции мы получаем доступ к переменной, без необходимости передачи информации в качестве параметра вызова.

Но в некоторых случаях, к сожалению, это является недостатком. Используя готовые функции, может оказаться так, что это же имя переменной одновременно используется и для других целей и из-за этого программа может работать неправильно.

Второй тип переменной – локальная переменная. Мы определяем ее в теле функции, и она доступна только на уровне этой функции. Локальная переменная инициализируется при вызове функции и уничтожается после завершения ее работы. Использование локальных переменных снижает спрос на оперативную память, но в то же время затрудняет передачу информации между различными функциями программы.

Настоятельно рекомендуется использовать локальные переменные и функции с параметрами вызова. Глобальные переменные следует использовать в тех ситуациях, когда одна и та же переменная используется в нескольких функциях.

Следующий код иллюстрирует место и способ декларации глобальных и локальных переменных:

char tablica; // глобальный массив доступный из любого места программы
void setup()
{
int a; // локальная переменная «a» доступна из функции setup()
}
int x; // глобальная переменная доступна из любого места программы
void loop()
{
int a; // локальная переменная «a» доступна из функции loop()
char b; // локальная переменная «b» доступна из функции loop()
} 

Как показано в приведенном выше примере, переменные, объявленные внутри функции, являются локальными переменными, а переменные, объявленные вне тела функций, являются глобальными переменными.

Переменная «а» в функции setup () — это совершенно другая переменная, чем «а» в функции loop().

Давайте рассмотрим другой код.

 Следующий код можно скомпилировать и запустить, а результат работы программы наблюдать с помощью монитора последовательного порта, доступного в Arduino IDE (функции Serial.begin и Serial.print предназначены для отправки данных через последовательный порт)

void setup()
{
Serial.begin(9600);    //инициализация последовательного порта
}
int a=10;    //глобальная переменная «а» со значением 10
void loop()
{
Serial.print(a);    //вывод переменной «а», 10
Serial.print(» «);
int a=1;    //локальная переменная «a» со значением 1
Serial.println(a);    //вывод переменной «а», 1
}

В этом примере есть дополнительная часть — глобальная переменная и локальная переменная с тем же именем. Компилятор не возвращает ошибку. Однако, необходимо помнить о проблемах, которые могут возникнуть из приведенного выше примера.

В начале функции loop() локальная переменная еще не объявлена. Обращаясь к переменной «а», мы обращаемся к глобальной переменной, значение которой составляет 10. После объявления локальной переменной «а» внутри функции loop (), обращаемся к ней и получаем значение 1. Функция loop() вызывается в системе циклически, поэтому с помощью монитора последовательного порта, мы можем наблюдать чередование появление чисел 10 и 1.

Изменим немного код:

void setup()
{
Serial.begin(9600);    //инициализация последовательного порта
}
int a=10;    //глобальная переменная «а» со значением 10
void loop()
{
Serial.print(a);    //вывод переменной «а», 10
Serial.print(» «);
int a=1;    //локальная переменная «a» со значением 1
a++;    //увеличить значение переменной «а» на единицу
Serial.println(a);    //вывод переменной «а»
}

Как видно на примере изменение значения переменной «а» относится к локальной переменной. Следует избегать использования локальных и глобальных переменных с одним и тем же именем, потому что это может создать потенциальные проблемы с нормальным функционированием программы.

Макроопределения

Мы можем единожды указать, что левый светодиод — это пин 13, правый — пин 12,
а переключать состояния нужно каждые X миллисекунд. Для этого каждому значению
назначается понятное имя, которое затем и используется для обращения:

#define LEFT_LED      13
#define RIGHT_LED     12
#define SWITCH_TIME   2000
 
void setup()
{
    pinMode(LEFT_LED, OUTPUT);
    pinMode(RIGHT_LED, OUTPUT);
}
 
void loop()
{
    digitalWrite(LEFT_LED, HIGH);
    digitalWrite(RIGHT_LED, LOW);
    delay(SWITCH_TIME);
 
    digitalWrite(LEFT_LED, LOW);
    digitalWrite(RIGHT_LED, HIGH);
    delay(SWITCH_TIME);
}

Всё, теперь для изменения параметров устройства достаточно изменить нужные
значения в начале программы и не думать об изменениях в самой логике. Кроме
того выражение вроде гораздо более
информативно нежели и даёт чёткое понимание того,
что имел в виду автор.

Конструкция называется макроопределением. Она говорит
компилятору о том, что всякий раз, когда он видит указанное имя, стоит
использовать на этом месте указанное значение.

Обратите внимание: макроопределения не завершаются точкой с
запятой в конце строки. Дело в том, что — это не обычное
выражение, а так называемая препроцессорная директива

Подстановка
конкретных значений вместо имён происходит ещё до компиляции, на стадии
предварительной обработки исходного файла. На этой стадии, по сути, компилятор
проделывает операцию как в текстовом редакторе: «Найти все и заменить». Просто
результат этой операции не сохраняется в файл, а тут же им используется для
непосредственной компиляции.

Если бы вы поставили после , в результате обработки компилятор
увидел бы такой код:

// Если завершать директивы точками с запятой…
#define LEFT_LED      13;
#define RIGHT_LED     12;
#define SWITCH_TIME   2000;
 
// …они появятся в самом неподходящем месте
void setup()
{
    pinMode(12;, OUTPUT);
    pinMode(13;, OUTPUT);
}
 
void loop()
{
    digitalWrite(12;, HIGH);
    digitalWrite(13;, LOW);
    delay(2000;);
 
    digitalWrite(12;, LOW);
    digitalWrite(13;, HIGH);
    delay(2000;);
}

Попытка скомпилировать такой скетч приведёт к ошибке.

Встроенные макроопределения

Вы могли догадаться, что уже знакомые нам значения , ,
— это также не что иное как макроопределения. Просто
для них написан в некотором другом месте и мы можем просто сразу ими
пользоваться.

На самом деле код мигания светодиодом:

void setup()
{
    pinMode(13, OUTPUT);
}
 
void loop()
{
    digitalWrite(13, HIGH);
    delay(100);
    digitalWrite(13, LOW);
    delay(900);
}

с точки зрения компилятора есть ни что иное как:

void setup()
{
    pinMode(13, 1);
}
 
void loop()
{
    digitalWrite(13, 1);
    delay(100);
    digitalWrite(13, );
    delay(900);
}

Если вы откроете файл в вашем
дистрибутиве Arduino IDE, вы сможете увидеть, что — это
макроопределение , — , OUTPUT — и т.д. Эти значения
используются настолько часто, что они встроены таким образом.

Использование понятных имён вместо магических чисел — это один из признаков
профессионализма. Это делает код более понятным и простым для изменений, что
всегда уважается.

Об именах макроопределений

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

// плохо
#define ledpin  13
#define led_pin 13
#define LedPin  13
 
// хорошо
#define LED_PIN 13

Это настолько привычное правило, что вы можете ввести в заблуждение других,
если не будете его придерживаться. Опять же, следование общепринятым канонам —
признак профи.

Стоит отметить, что язык C++, как и многие другие считает строчные и заглавные
буквы различными, поэтому если к макроопределению вы затем
попытаетесь обратиться, написав будет выдана ошибка компилятора о
том, что он не понимает что такое . То же самое касается имён
функций и всего остального.

4Использование библиотеки ArduinoThreadдля создания параллельных потоков

Чтобы решить поставленную задачу, воспользуемся замечательной библиотекой ArduinoThread, которая позволяет с лёгкостью создавать псевдо-параллельные процессы. Она работает похожим образом, но позволяет не писать код по проверке времени – нужно выполнять задачу в этом цикле или не нужно. Благодаря этому сокращается объём кода и улучшается читаемость скетча. Давайте проверим библиотеку в действии.

Библиотека ArduinoThread

Первым делом скачаем с официального сайта архив библиотеки и разархивируем его в директорию libraries/ среды разработки Arduino IDE. Затем переименуем папку ArduinoThread-master в ArduinoThread.

Схема подключений останется прежней. Изменится лишь код программы.

#include <Thread.h>  // подключение библиотеки ArduinoThread
const int soundPin = 3;  // переменная с номером пина пьезоэлемента
const int ledPin = 13;  // переменная с номером пина светодиода

Thread ledThread = Thread(); // создаём поток управления светодиодом
Thread soundThread = Thread(); // создаём поток управления сиреной

void setup() {
    pinMode(soundPin, OUTPUT); // объявляем пин 3 как выход.
    pinMode(ledPin, OUTPUT);   // объявляем пин 13 как выход.

    ledThread.onRun(ledBlink);  // назначаем потоку задачу
    ledThread.setInterval(1000); // задаём интервал срабатывания, мсек
    
    soundThread.onRun(sound);     // назначаем потоку задачу
    soundThread.setInterval(20); // задаём интервал срабатывания, мсек
}

void loop() {
    // Проверим, пришло ли время переключиться светодиоду:
    if (ledThread.shouldRun())
        ledThread.run(); // запускаем поток
    
    // Проверим, пришло ли время сменить тональность сирены:
    if (soundThread.shouldRun())
        soundThread.run(); // запускаем поток
}

// Поток светодиода:
void ledBlink() { 
    static bool ledStatus = false;    // состояние светодиода Вкл/Выкл
    ledStatus = !ledStatus;           // инвертируем состояние
    digitalWrite(ledPin, ledStatus);  // включаем/выключаем светодиод
}

 // Поток сирены:
void sound() { 
    static int ton = 100;  // тональность звука, Гц
    tone(soundPin, ton);  // включаем сирену на "ton" Гц
    if (ton }

В программе мы создаём два потока – ledThread и soundThread, каждый выполняет свою операцию: один мигает светодиодом, второй управляет звуком сирены. В каждой итерации цикла для каждого потока проверяем, пришло ли время его выполнения или нет. Если пришло – он запускается на исполнение с помощью метода run(). Главное – не использовать оператор delay(). В коде даны более подробные пояснения.

Параллельное выполнение потоков на Arduino

Загрузим код в память Ардуино, запустим. Теперь всё работает в точности так, как надо!

Язык программирования

Язык программирования Ардуино довольно прост в освоении, так как основной целевой аудиторией его применения являются любители. Однако считается одним из самых лучших языков для программирования микроконтроллеров.

Внимание! Для начала работы необходимо установить среду программирования Arduino IDE.

Arduino IDE является бесплатной программой, скачать которую может любой желающий. На нашем сайте вы можете скачать любую подходящую для вас версию среды. Также доступ к скачиванию IDE предоставлен на официальном сайте компании, а при желании, разработчиков можно отблагодарить, сделав денежный перевод.

Программу, написанную на языке программирования Ардуино называют скетчем. Готовые скетчи записываются на плату для их выполнения.

Среда IDE поддерживается такими операционными системами, как Windows, MacOs и Linux. На официальном сайте компании указанно, что данный язык программирования написан на Wiring, но на самом деле его не существует и для написания используется C++ с небольшими изменениями.

Какие ещё языки используют для Arduino

Но чу! Под Arduino мож­но писать и на дру­гих язы­ках!

С. Как и С++, Си лег­ко мож­но исполь­зо­вать для про­грам­ми­ро­ва­ния мик­ро­кон­трол­ле­ров Arduino. Толь­ко если С++ не тре­бу­ет ника­ких допол­ни­тель­ных про­грамм, то для С вам пона­до­бит­ся , что­бы пра­виль­но пере­ве­сти код в язык, понят­ный кон­трол­ле­рам AVR.

Python. Было бы стран­но, если бы тако­му уни­вер­саль­но­му язы­ку не нашлось при­ме­не­ния в робо­то­тех­ни­ке. Берё­те биб­лио­те­ки PySerial и vPython, при­кру­чи­ва­е­те их к Python и гото­во!

Java. Прин­цип такой же, как в Python: берё­те биб­лио­те­ки для рабо­ты с пор­та­ми и кон­трол­ле­ра­ми и мож­но начи­нать про­грам­ми­ро­вать.

HTML. Это, конеч­но, совсем экзо­ти­ка, но есть про­ек­ты, кото­рые застав­ля­ют HTML-код рабо­тать на Arduino.

А вооб­ще Arduino рабо­та­ет на кон­трол­ле­рах AVR, и про­шить их мож­но любым кодом, кото­рый ском­пи­ли­ро­ван под это желе­зо. Всё, что вам нуж­но — най­ти биб­лио­те­ку для ваше­го люби­мо­го язы­ка, кото­рая пре­об­ра­зу­ет нуж­ные коман­ды в машин­ный код для AVR.

Главное — алгоритмы

Любой робот — это один боль­шой алго­ритм. Что­бы научить­ся думать как про­грам­мист и писать свои алго­рит­мы с нуля, при­хо­ди­те в Прак­ти­кум.
Попро­бо­вать

Функция delay()

Далее, мы освоим функцию delay(). Она простая и  будет использоваться для контроля того, как долго будет установлен вывод на HIGH:

Функция delay() сообщает Arduino остановиться и ждать. Число в скобках обозначает длину задержки в миллисекундах (1 секунда = 1000 миллисекунд).

В этот момент в программе светодиод горит в течение одной секунды.

Чтобы отключить его, мы можем использовать digitalWrite() с параметром LOW в качестве второго аргумента:

Функция delay() будет сохранять состояние напряжения НИЗКИМ в течение 1000 миллисекунд.

Поскольку две функции digitalWrite() и delay() используются в void loop(), код будет выполняться снова и снова, заставляя светодиод мигать, т.е. включаться и выключаться.

Функция digitalWrite() действительно очень полезна, когда помещается в тело условного оператора.

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

Простыми словами, мы используем условные операторы, когда нужно сделать X, если Y. Например: если A > 5, то Y = 10.

Но вернемся к основной теме урока — код ниже установит цифровой вывод на режим ВЫСОКИЙ, когда показание датчика будет больше 50:

В примере выше если переменная sensorReading больше 50, будет выполнена функция digitalWrite(), и outputPin будет установлен в состояние ВЫСОКОГО напряжения.

Если переменная sensorReading меньше 50, код в операторе if будет проигнорирован, и функция digitalWrite() не будет выполнена.

В следующих уроках мы изучим еще ряд навыков, которые лучше знать на старте изучения Ардуино.

Функции и методы класса String

Для работы со строками в String предусмотрено множество полезных функций. Приведем краткое описание каждой из них:

  • String() – конструктор, создает элемент класса данных string. Возвращаемого значения нет. Есть множество вариантов, позволяющих создавать String из строк, символов, числе разных форматов.
  • charAt() возвращает указанный в строке элемент. Возвращаемое значение – n-ный символ строки.
  • compareTo() – функция нужна для проверки двух строк на равенство и позволяет выявить, какая из них идет раньше по алфавиту. Возвращаемые значения: отрицательное число, если строка 1 идет раньше строки 2 по алфавиту; 0 – при эквивалентности двух строк; положительное число, если вторая строка идет раньше первой в алфавитном порядке.
  • concat() – функция, которая объединяет две строки в одну. Итог сложения строк объединяется в новый объект String.
  • startsWith() – функция показывает, начинается ли строка с символа, указанного во второй строке. Возвращаемое значение: true, если строка начинается с символа из второй строки, в ином случае false.
  • endsWith() – работает так же, как и startsWith(), но проверяет уже окончание строки. Также возвращает значения true и false.
  • equals() – сравнивает две строки с учетом регистра, т.е. строки «start» и «START» не будут считаться эквивалентными. Возвращаемые значения: true при эквивалентности, false в ином случае.
  • equalsIgnoreCase() – похожа на equals, только эта функция не чувствительна к регистру символов.
  • getBytes() – позволяет скопировать символы указанной строки в буфер.
  • indexOf() –  выполняет поиск символа в строке с начала. Возвращает значение индекса подстроки val или -1, если подстрока не обнаружена.
  • lastIndexOf() –выполняет поиск символа в строке с конца.
  • length() – указывает длину строки в символах без учета завершающего нулевого символа.
  • replace() – заменяет в строке вхождения определенного символа на другой.
  • setCharAt() – изменяет нужный символ в строке.
  • substring() – возвращает подстроку. Может принимать два значения – начальный и конечный индексы. Первый является включительным, т.е. соответствующий ему элемент будет включаться в строку, второй – не является им.
  • toCharArray() – копирует элементы строки в буфер.
  • toLowerCase() – возвращает строку, которая записана в нижнем регистре.
  • toUpperCase() – возвращает записанную в верхнем регистре строку.
  • toInt() – позволяет преобразовать строку в число (целое). При наличии в строке не целочисленных значений функция прерывает преобразование.
  • trim() – отбрасывает ненужные пробелы в начале и в конце строки.
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector