No Image

Arduino функции возвращаемые значения

СОДЕРЖАНИЕ
0 просмотров
12 декабря 2019

analogRead()

Функция analogRead() возвращает целочисленное значение в диапазоне от 0 до 1023, пропорциональное напряжению, поданному на аналоговый вход, номер которого мы передаем функции в качестве параметра pinA. В большинстве плат это порты 0-5. В платах Mini и Nano: 0-7, в плате Mega: 0-15.

analogWrite()

Устанавливает аналоговое значение PWM (ШИМ, Широтно-импульсная модуляция) для вывода. Используется для плавного изменения цвета у светодиода или скорости вращения мотора. Пины с поддержкой PWM обозначены на плате символом тильда (

). Для Arduino Uno выводы имеют номера 3, 5, 6, 9, 10 и 11.

Перед вызовом данной функции нет необходимости вызывать функцию pinMode().

Функция не возвращает значения и имеет два параметра.

  • pin — номер вывода для отправки сигнала
  • value — значение яркости от 0 (полностью выключен) до 255 (полная яркость) (значение скважности ШИМ)

bitRead()

Функция считывает биты с указанного числа. Возвращает значение бита (0 или 1)

  • x — число, которое нас интересует
  • n — какой бит нужно считать

delay()

Функция делает паузу в программе на указанное количество времени в миллисекундах , которое указывается в единственном параметре. Не возвращает значения.

  • ms — число миллисекунд для установки паузы (unsigned long). 1000 миллисекунд = 1 сек

digitalRead()

Функция digitalRead() считывает показания с цифрового вывода. Возвращается HIGH (высокое, 1) или LOW (низкое, 0):

  • pin — номер цифрового порта, на который мы отправляем сигнал

digitalWrite()

Функция digitalWrite() не возвращает значения и принимает два параметра:

  • pin — номер цифрового порта, на который мы отправляем сигнал
  • value — значение, которое мы отправляем на порт. Для цифровых портов значением может быть HIGH (высокое, 1) или LOW (низкое, 0)

После выполнения setup() запускается функция loop(), которая выполняется в бесконечном цикле.

Функция loop() должна присутствовать в любой программе (скетче), даже если вам не нужно ничего выполнять в них — просто не пишите ничего между фигурными скобками.

Преобразует число из одного диапазона в другой диапазон. Т.е. значение из fromLow попадёт в toLow, значение fromHigh попадёт в toHigh, а значения между ними пропорционально попадут в новые значения другого диапазона.

Нижнее значение диапазона может быть больше или меньше верхнего значения. Функция map() в таких случаях может работать в обратном порядке. Например.

Также допускаются отрицательные числа.

Функция использует целые числа и не генерирует дробные числа. Дробные числа усекаются до целых.

  • value — число для конвертации
  • fromLow — нижнее значение текущего диапазона
  • fromHigh — верхнее значение текущего диапазона
  • toLow — нижнее значение нового диапазона
  • toHigh — верхнее значение нового диапазона

Возвращается новое значение после преобразования.

millis()

Функция без параметров возвращает число миллисекунд (unsigned long), прошедших с запуска Arduino.

pinMode()

Функция pinMode() устанавливает режим для портов.

  • pin — вывод, с которым мы собираемся работать
  • mode — как должен работать указанный вывод: работать на выход (OUTPUT) или вход (INPUT) или INPUT_PULLUP

random()

Функция генерирует псевдослучайные числа.

  • min — нижняя граница случайных значений
  • max — верхняя граница случайных значений

Функция возвращает случайное число между min и max-1 (long)

setup()

Функция setup() выполняется один раз при запуске микроконтроллера. Обычно она используется для конфигурации портов микроконтроллера и других настроек.

Функция setup() должна присутствовать в любой программе (скетче), даже если вам не нужно ничего выполнять в них — просто не пишите ничего между фигурными скобками.

shiftOut()

В качестве третьего аргумента передаётся параметр bitOrder (порядок битов), который определяет в какой последовательности подаваемые биты будут интерпретироваться регистром — в прямом или обратном. LSBFIRST (Least Significant Bit First) — означает, что вывод в регистр начнётся с последнего бита. Например, при передаче байта 00010111 на выходах регистра окажутся значения (с 1 по 8 пины) — 00010111. MSBFIRST (Most Significant Bit First) — означает, что вывод в регистр начнётся с первого бита. При передаче байта 00010111 на выходах регистра окажутся значения в обратном порядке (с 1 по 8 пины) — 11101000.

Генерирует звук заданной частоты на указанном пине. Можно указать продолжительность звука. Если продолжительность не указана, то остановить воспроизведение можно с помощью функции noTone(). Вывод можно соединить с пьезопищалкой или другим устройством, способным выводить звук.

Можно выводить только одну ноту во время исполнения.

  • pin — вывод платы для воспроизведения звука
  • frequency — частота звука в герцах (unsigned int)
  • duration — продолжительность в миллисекундах (необязательно) — unsigned long

Функция не возвращает значения.

Serial

Класс, позволяющий общаться с компьютером.

available()

Получает число байт (символов), доступных для чтения из последовательного порта. Данные уже пришли и хранятся в специальном буфере (64 байта).

begin()

Устанавливает связь с портом для считывания данных на заданной скорости с Arduino на ваш компьютер. В IDE есть выпадающий список, в котором можно увидеть возможные варианты скоростей.

  • speed — скорость бит в секунду (long)
Читайте также:  Аккумуляторный бур для зимней рыбалки

flush()

Очищает входной буфер последовательного порта. Находящиеся в буфере данные теряются, и дальнейшие вызовы Serial.read() или Serial.available() будут иметь смысл для данных, полученных после вызова Serial.flush().

Печатает данные, поступаемые с серийного порта в виде ASCII-текста без символа перевода строки. Схожа с функцией Serial.println().

  • val — значение для печати
  • format — формат выводимых данных. Можно использовать константы DEC (десятичная система), HEX (шестнадцатеричная), OCT (восьмеричная), BIN (бинарная)

println()

Печатает данные, поступаемые с серийного порта в виде ASCII-текста. Данные заканчиваются символом перевода строки (ASCII 13, ‘
‘) и новой строки (ASCII 10, ‘
‘). Схожа с функцией Serial.print().

  • val — значение для печати
  • format — формат выводимых данных. Можно использовать константы DEC (десятичная система), HEX (шестнадцатеричная), OCT (восьмеричная), BIN (бинарная)

Считывает входящие данные из последовательного порта.

Возвращает первый байт входящих данных, если они есть или -1, если данные не доступны.

write()

Записывает данные в последовательный порт. Данные посылаются как байт или последовательность байт; для отправки символьной информации следует использовать функцию print().

  • val: переменная для передачи, как единственный байт
  • str: строка для передачи, как последовательность байт
  • buf: массив для передачи, как последовательность байт
  • len: длина массива

Разное

pulseIn() — Возвращает продолжительность в микросекундах следующего импульса с напряжением HIGH на заданном контакте

noTone() — Прерывает любые серии импульсов, запущенные вызовом tone

micros() — Действует подобно millis, но возвращает число микросекунд, прошедших с момента последнего сброса платы. Значение обнуляется примерно через 70 минут

delayMicroseconds() — минимальная задержка составляет 3 мкс, максимальная — около 16 мс

attachInterrupt() — Устанавливает функцию myFunction, как обработчик положительного фронта прерывания 1 (контакт D3 в UNO)

detachInterrupt() — Запрещает обработку сигналов от прерывания 1

  • Уроки
  • Программирование
  • Функции

Функции

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

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

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

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

Функция может принимать параметры, может не принимать, может возвращать какое-то значение, может не возвращать. Давайте рассмотрим эти варианты.

“Типы” функций

Функция, которая ничего не принимает и ничего не возвращает

Самый простой для понимания вариант, с него и начнём. Помимо типов данных, которые я перечислял в уроке о типах данных, есть ещё один – void, который переводится с английского как “пустота”. Создавая функцию типа void мы указываем компилятору, что никаких значений возвращаться не будет (точнее будет – функция вернёт “ничего”). Давайте напишем функцию, которая найдёт сумму двух чисел и присвоит её третьему числу. Так как функция у нас без параметров и ничего не возвращает, переменные придётся объявить заранее и сделать их глобальными, иначе функция не будет иметь к ним доступ и мы получим ошибку:

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

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

Функция, которая ничего не принимает и возвращает результат

Чтобы функция могла вернуть численное значение, она должна быть описана с типом данных, который будет возвращаться. Нужно заранее подумать, какой тип будет возвращён, чтобы избежать ошибок. Например я знаю, что моя суммирующая функция работает с типом данных byte, она складывает два таких числа. Это означает, что результат вполне может превысить лимит на тип данных byte (сложили 100 и 200 и вот, уже 300), значит функции следовало бы возвращать например тип данных int. Собственно поэтому у переменной c тип данных тоже int.

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

Читайте также:  Газовый котел двухконтурный напольный конорд

Давайте перепишем наш код так, чтобы числа a и b складывались и результат возвращался функцией, и этот результат мы уже “ручками” приравняем к c.

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

Функция, которая принимает параметры и возвращает результат

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

И вот так мы получили универсальную функцию sumFunction, которая принимает две величины типа byte, складывает их и возвращает. Это и есть выполнение концепции “отделение кода от данных”, функция живёт сама по себе и не зависит от других переменных!

Казалось бы, можно использовать функцию как sumFunction(100, 200), и она вернёт значение 300. Но не так всё просто, потому что целое число по умолчанию имеет тип данных int, и при попытке передать такой вот int в нашу функцию, которая принимает byte, мы получим ошибку, в которой будет написано, что нельзя передать int вместо byte. В этом случае можно привести тип числа к byte вручную, вот так это будет выглядеть:

А как быть, если мы хотим складывать уже имеющийся функцией другие типы данных? Например float. Можно преобразовать типы данных при передаче параметров, но функция всё равно вернёт целое число. Сделать нашу функцию ещё более универсальной сможет такая штука C++ как перегруженная функция.

Перегруженные функции

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

Итак, у нас теперь целых три функции с одинаковым именем, но разными наборами параметров и типов возвращаемого значения. Программа сама разберётся, какую из функций использовать, на основе передаваемых параметров. Передали два float – работает третья функция, вернёт float. Передали три int – получили их сумму при помощи второй по счёту функции. Передали два int – получили их сумму при помощи первой функции. Вот такая удобная штука!

Шаблонные функции

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

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

Описание функции можно раскрыть:

Сделаем функцию, которая возвращает квадрат числа любого типа

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

В некоторых ситуациях шаблоны могут быть очень удобны!

Также при вызове шаблонной функции можно вручную передать тип данных:

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

Макро-функции

Вы наверное уже помните такую директиву препроцессору, как #define. Из урока про переменные и константы мы узнали, что при помощи дефайна можно задавать константы. Ключевая особенность работы define заключается в том, что он заменяет последовательность символов чем угодно, что мы там напишем, и это даёт возможность создавать так называемые макро-функции (macro), которые не создаются как функции, а вставляются в код при компиляции. Например вот так будет выглядеть макро, складывающая два числа:

На этапе компиляции в данном документе с кодом все встречающиеся sum(значение1, значение2) будут заменены на значение1 + значение2, таким образом это будет сумма

Зачем это нужно? Обычная функцию с именем, как разбиралось выше, имеет свой “адрес” в памяти, и при каждом вызове функции ядро обращается к ней по этому адресу, что занимает какое-то время. Макро функция же “встраивается” в код программы и выполняется сразу же. В то же время, если макро-функция вызывается в нескольких местах программы, она займёт больше места, чем отдельная обычная функция, т.к. все вызовы будут заменены на код. Делать макросы имеет смысл на простые функции (как в примере ниже), на редко вызываемые функции, и в тех местах, где важно максимальное быстродействие, т.е. важен каждый такт процессора.

Читайте также:  Developer android com studio index html

В “языке” Arduino есть несколько готовых инструментов, которые кажутся функциями, но на самом деле являются макро. Заглянем в Arduino.h и увидим следующее:

Вот так вот, всем нам знакомые функции оказались макросами!

А ещё можно “переносить строку” для создания макро-функций с большим количеством текста, перенос делается при помощи обратного слэша –

Обратите внимание, на последней строке перенос уже не нужен! Таким образом, во время работы препроцессора все вызовы “функции” printWords будут заменены на указанные строки кода. Можно аргументы таким же образом передавать:

Как вы можете видеть, объявлять большую макро-функцию неудобно, и тут на помощь приходят встроенные функции.

Встроенные функции

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

Объявляется встроенная функция очень просто: достаточно дописать ключевое слово inline перед объявлением функции. Компилятор может отказаться встраивать функцию (зависит от его настроек), поэтому можно попросить его принудительно встроить функцию при помощи атрибута __attribute__((always_inline)) . Таким образом, для объявления встроенной функции нужно в самом начале написать inline __attribute__((always_inline)) . Рассмотрим функцию, которая просто увеличивает значение переменной:

Всё! Теперь вызовы incr будут заменены на код.

Если нужно разделить описание и реализацию, то можно записать так:

Статические функции

В уроке про переменные и типы данных мы обсуждали спецификатор static, который позволяет скрыть глобальную переменную от других файлов программы. С функцией спецификатор static делает то же самое – статическая функция скрыта от вызова из других файлов, т.е. её область определения – файл, в котором она находится. static void printHello();

Передача массива в функцию

Иногда бывает нужно передать в функцию массив (мы о них уже говорили), передать именно массив целиком, а не отдельный его элемент. В этом случае уже не обойтись без указателей (читай урок про указатели). В следующем примере наша функция sumFunction будет суммировать элементы массива, который в неё передаётся. Функция заранее знает, сколько в массиве элементов, потому что я явно цифрой указал количество в цикле for.

Что из этого нужно запомнить: при описании функции параметр массива указывается со звёздочкой, т.е. * . При вызове массив передаётся как . И в целом всё.

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

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

Указатель на функцию

Указатели – очень сложная тема, которая практически не пригодится новичку, но некоторые алгоритмы знать нужно. Например передачу указателя на функцию, это полезно при создании своих функций в стиле ардуиновской attachInterrupt, в которой мы указываем свою функцию, которую сами создаём, а она вызывается в другом месте. Сделать это можно так:

Описание и реализация

Хорошим тоном считается объявлять функции отдельно от реализации. Что это значит: в начале документа, или в отдельном файле, мы описываем функцию (это будет называться прототип функции), а в другом месте пишем реализацию. Так делают в серьёзных программных проектах, Ардуино-библиотеки – не исключение. Также такое написание позволяет слегка ускорить компиляцию кода, потому что компилятор уже знает, что он в нём найдёт. Алгоритм такой:

Описание (прототип) функции

Реализация функции

То бишь всё то же самое, но с телом функции и фигурными скобками. Пример:

Примечание: хоть компилятор и позволяет вызывать функцию до её объявления (по порядку написания кода), иногда он может не найти функцию и выведет ошибку “функция не объявлена”. В этом случае достаточно сделать прототип функции и расположить его поближе к началу документа, да и вообще все используемые функции можно сделать в виде прототипов и вынести в начало кода, вместе с глобальными переменными!

Видео