Статья предназначена для начинающих программистов на Ардуино. Конечно можно копировать проекты из сети и воплощать их в жизнь. Но что бы создать свой, или соединить в одно целое скаченные куски кода нужно понимать, как все это работает, хотя бы поверхностно.
Как подключить и прошить, способы питания Ардуино есть ссылка в конце этой статьи.
Основой платы Ардуино является микроконтроллер. Рассмотрим на примере Ардуино NANO
Смысл программирования микроконтроллера, а значит и Ардуино:
- обработка информации - вывод обработанной информации
- ввод информации - обработка введенной информации - вывод обработанной информации
Все вышесказанное осуществляется через порты (ножки, Pin) микроконтроллера, а порты микроконтроллера физически соедены с ножками платы ардуино плюс обвязка, то есть вспомогательные электронные компоненты. Получаем отладочную плату Ардуино
Как мы видим порты имеют свое предназначение. например I2C, UART протоколы передачи данных, то есть определенного потока 0 и 1. итд...Все спец назначение портов выполнено при изготовлении. Но обо всем по порядку. Ни что так не отбивает охоту начала программирования, как не совсем понятное.
ЛЮБОЙ порт Ардуино, желтые овалы, мы можем запрограммировать как на вывод информации pinMode(Pin, OUTPUT); , так и на ввод pinMode(Pin, INPUT);.
По умолчанию, все пины сконфигурированы как входы (INPUT)
Одним из языков программирования микроконтроллеров является С++, но сложен для начинающих. По этому на С++ сверху надели шляпу в виде фреймворка Wiring и получился язык программирования Ардуино , более простой для начинающих. Осуществляется он в среде разработки Arduino IDE
Простыми словами, нужно собрать схему, написать для нее программу управления, Скетч (набросок), или несколько скетчей и библиотек (проект), залить все это в плату Ардуино с помощью Arduino IDE то есть прошить. Устройство готово.(или прототип устройства)
Программирование на Ардуино можно разделить на три основных раздела: Данные, Операторы, Функции.
Данные состоят из переменных и констант.
Переменные(не постоянное)
То есть переменные могут менять свое значение в ходе выполнения программы. Переменные это произвольно придуманное Вами уникальное название. Для экономии памяти имеют размер - тип, который прописывается перед именем переменной, например, byte MyPeremen = 12 ; . Рассмотрим. byte это тип, занимает в памяти 1 байт (8 бит), можно записать число 0 - 255. MyPeremen уникальное , придуманное Вами имя. Присваиваем нашей переменной первоначальное значение = 12. Это не равно, это оператор присваивания, равно будет вот так ==, вернее сравнение, но об этом чуть позже. Можно ни чего не присваивать, byte MyPeremen ; , присвоим в ходе выполнения программы.
Есть знаковые типы данных, есть беззнаковые, есть с плавающей точкой. Давайте посмотрим
Тип char для вывода символа на дисплей. Есть string, для вывода строки, рассмотрим когда будем изучать вывод на дисплей.
Массив (array) – это совокупность переменных одного типа. К ним можно обратиться с помощью общего имени и номера элемента в массиве. Простыми словами это набор переменных, которые называются одним именем и имеют личные номера. Объявляем массив так
тип имя_массива [набор_переменных];
Указать Ардуино ИДЕ размер массива можно так:
явным числом указываем количество ячеек
byte MyArray[6];
Указываем содержимое ячеек. Ардуино IDE сама посчитает их количество
byte MyArray[] = {2, 4, 8, 10, 34,};
указываем и то и другое. Количество ячеек в [ ] должно совпадать с { }
byte MyArray[3] = {1, 3, 99};
Константа (постоянное)
- LOW в скетче логический 0. На входе порта принимает значение логического 0 от 0 вольт до 2,6 волт
- HIGH в скетче логическая 1. На входе порта принимает значение логической 1 при 2,6 - 5 вольт
- INPUT, OUTPUT мы уже знакомы, настраивает порт на вход или выход.
- INPUT_PULLUP порт на вход, подтянуть к ножке внутреннее сопротивление 20 ком к положительному питанию
- true Обычно говорят, что true — это 1 или истина
- false определяется как 0 в логическом выражении или лож
Можем так же объявить свою константу, например const byte MyConst = 25; , занимает в оперативной памяти 1 байт (byte).
Переменные, константы и другие создаваемые Вами данные имеют такое важное понятие, как время "жизни" и область видимости. Она бывает глобальной и локальной.
Глобальные данные:
- Объявляется обычно в начале программы.
- Доступна для чтения и записи в любом месте программы.
- "живет" в оперативной памяти на всём протяжении работы программы, то есть не теряет своё значение.
Локальные данные:
- .Объявляется внутри любого блока кода, заключённого в { фигурные скобки }
- Доступна для чтения и записи только внутри своего блока , и во всех вложенных в него.
- Находится в оперативной памяти с момента объявления и до выхода из блока, то есть удаляется из памяти и её значение стирается.
Операторы и Функции
Их великое множество. Требуется много отдельных статей. По мере написания будут ТУТ
Что бы понимать дальнейший материал изучим некоторые
setup() Оператор вызывается, когда стартует скетч. Используется для инициализации переменных, определения режимов работы выводов, и т.д. . Запускает только один раз, после каждой подачи питания или сброса платы Arduino.
loop() Оператор зацикливает скетч. В цикле выполняется все ветвления и вычисления.
if Оператор используется в сочетании с операторами сравнения, проверяет, достигнута ли истинность условия
Операторы сравнения
x == y (x равно y), не путать с "=", это оператор присваивания
x != y (x не равно y)
x < y (x меньше чем y)
x > y (x больше чем y)
x <= y (x меньше чем или равно y)
x >= y (x больше чем или равно y)
if (условие) { если условие выполнено мы тут} ; не выполнено мы тут ;
if ...... else , то же самое, но более гибкий. Давайте пошутим. Программист ложась спать ставит на тумбочку два стакана, пустой и полный. Если захотел пить берет полный, если не захотел пить, берет пустой.
if (захотел пить)
{
берет полный ;
}
else
{
берет пустой ;
}
Библиотека Serial содержит набор функции для работы с последовательным портом Arduino или иным внешним устройством по протоколу передачи данных UART (RX TX). Любая плата Arduino содержит один или несколько последовательных портов, служащих для связи с компьютером или с другими устройствами,
Объяснение требует отдельной статьи. Сегодня рассмотрим следующее :
Serial.begin(скорость); Инициирует последовательное соединение и задает скорость передачи данных в бит/c (бод). Для обмена данными с компьютером используйте следующие значения: 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600 или 115200.
Serial.print(); Передает данные через последовательный порт в компьютера, в монитор порта Ардуино IDE
ФУНКЦИИ
digitalRead(Pin) Функция считывает значение с заданного входа - HIGH или LOW, то есть 0 или 1.
digitalWrite(pin, value) Подает HIGH или LOW значение на цифровой
выход (pin).
Можно создать свою функцию. Функция – блок кода, который выполняет определенную задачу, обычно часто повторяющейся задачи. Синтаксис тип_данных имя_функции (аргументы){тело функции};. Функция может возвращать значение, а может не возвращать, может передавать аргументы, может не передавать. Пока что запоминаем, если функция ни чего не возвращает впереди пишем void
Все изучим. Подробные объяснения будут ТУТ.
Что бы все это лучше понять, давайте наконец то начнем программировать.
Подключаем свою Ардуино к компьютеру и пишем скетч.
Прошиваем, открываем монитор порта , наблюдаем следующее
Разбираемся.
byte MyGlobal = 99 ; объявляем глобальную переменную, написав ее в начале скетча. Присваиваем ей первоначальное 99. Обратите внимание на символ ";" в конце строки.
Serial.println(MyGlobal); В теле void setup() выводим в монитор порта Ардуино IDE значение byte MyGlobal, то есть 99. Тело какой либо функции это все, что находится между фигурными скобками "{это_тело}"
void loop() оператор зацикливания. В нем пишется основной код.
void Result(); объявляем свою функцию. В теле этой функции, то есть между { } объявляем две локальных переменных, присваиваем им значение 200 и 2. В следующей строке MyGlobal = MyLocal + MyLocal_2; суммируем две локальных переменных и результат помещаем в глобальную переменную MyGlobal. В строке MyLocal ++; увеличиваем локальную переменную MyLocal на 1. Пока эта строка не имеет смысла, при выходе из тела нашей функции значение наших локальных переменных сотрутся из памяти. Останется только 202 в MyGlobal, так как она глобальная переменная, она "жива" за пределами нашей функции.
Serial.println( MyGlobal); выводим в порт значение 202 , задержка на пол секунды delay(500); и возвращаемся в начало лооп. Все повторяется до бесконечности пока не отключим или не перегрузим.
Давайте по экспериментируем, подправим наш скетч. Перенесем byte MyLocal = 200; из функции в начало скетча, то есть сделаем глобальной. Не забудьте стереть или закоментировать byte MyLocal = 200; , где она была. // byte MyLocal = 200;
Прошиваем, смотрим монитор порта.
Думаю догадываетесь почему все теперь по другому.
Попробуйте вместо byte MyGlobal = 99; написать unsigned long MyGlobal = 99; . хватит ли Вам терпения дождаться до 4 294 967 295, когда переполнится. Переполнение показано только в учебных целях, это вещь опасная, следите за этим.
И так мы готовы к нашей плате Ардуино подключать физические(аппаратные) компоненты. Начнем на первый взгляд с самого простого, КНОПКИ. При нажатии на кнопку физически соединяется контакт и замыкается какая то электрическая цепь. Так как нам нужен сигнал или 0 или 1(правило цифровой техники), посмотрим вот такую схему.
Верхняя кнопка не нажата. Через R1 +5 вольт создает высокий уровень. Нижняя кнопка нажата. "Земля", пересиливая R2 создает низкий уровень. Резистор называется подтягивающим к положительному питанию.
Но к Ардуино мы не будем паять резистор, а вспомним константу INPUT_PULLUP порт на вход, подтянуть к ножке внутреннее сопротивление 20 ком к положительному питанию. Резистор где то там, внутри. Отлично, делаем так
Добавляем в наш скетч несколько строк. Скачать ТУТ
Прошиваем Смотрим монитор порта Ардуино IDE
Все просто. Создаем локальную переменную byte ButtonState; Записываем состояние кнопки, то есть состояние ножки 12 ButtonState = digitalRead(12); , затем с помощью if...... else наша программа принимает решение, называется ветвление, зажигать и декремент MyLocal --; или гасить и инкремент MyLocal ++;.
Все просто да не просто. Понаблюдайте внимательно. Светодиод зажигается и гаснет с визуальной задержкой. Все дело в delay(1000); //задержка 1 сек. Это уйма времени, 16000000 тактов процессора. Большую часть наша программа находится там. Мы нажимаем, а из delay(1000); программа еще не вышла. Эта задержка нам нужна, что бы наблюдать за монитором порта .
Все за монитором порта больше наблюдать не нужно, удаляем или закомментируем delay(1000); . Светодиод зажигается и гаснет в РЕАЛЬНОМ ВРЕМЕНИ.
Но прежде чем устойчиво зажечься или погаснуть светодиод сотни, а может и тысячи раз загорится и погаснет. Мы этого не видим. Все дело в ДРЕБЕЗГЕ КОНТАКТОВ. Прежде чем перейти в устойчивое состояние 0 или 1 при нажатии или отпуске, примерно 5 - 70 мкс на выходной ножке кнопки чередуется сигнал 0 и 1. Это неприятное физическое явление.
Казалось бы да и ладно, мы же этого не видим. А если мы с помощью кнопки делаем ветвление программы, например заходим в меню. За 70 мкс программа может оказаться где ? Не предсказуемо.
Как бороться с этой неприятностью мы поговорим в следующих занятиях.
Борьба с дребезгом контактов будет ТУТ
Вывод информации на дисплей будет ТУТ.