Записки программиста
Это было неизбежно. Увлекшись электроникой, я должен был рано или поздно дойти и до программирования микроконтроллеров. А что может быть проще программирования AVR-микроконтроллеров в устройствах Arduino? Не удивительно, что начать я решил именно с них.
Что же из этого получилось — читайте далее.<\p>
Примечание: Описанные далее действия производились под Linux, на Arduino Uno и, соответственно, используемом в этом устройстве микроконтроллере ATmega328P.
Однако все написанное далее справедливо и для других моделей Arduino, а также мало отличаться в случае, если вы используете другую ОС.
Настройка Arduino IDE
Подключаем Arduino к компьютеру через USB порт и определяем при помощи dmesg имя соответствующего устройства. У меня это ttyACM0. Если увидели что-то вроде:
usb 1-1: new full-speed USB device number 59 using xhci_hcd usb 1-1: Device not responding to setup address. usb 1-1: device not accepting address 59, error -71
usb usb1-port1: unable to enumerate USB device
… значит проблема в USB-кабеле, используйте другой.
Смотрим, какие права стоят на это устройство:
В разных дистрибутивах результат немного отличается. В Arch Linux я увидел:
crw-rw—- 1 root uucp 166, 0 Jan 1 21:34 /dev/ttyACM0
То есть, для доступа к устройству пользователя нужно добавить в группу uucp:
sudo usermod -a -G uucp eax
В Ubuntu название группы будет другим, а именно «dialout». После добавления пользователя в группу скорее всего придется перелогиниться. В выводе id должны видеть, что ваш пользователь теперь состоит в группе.
Качаем Arduino IDE отсюда и распаковываем куда-нибудь:
cd ~/temp
tar –xz -xvf arduino-1.8.0-linux64.tar.xz
mkdir ~/opt
mv arduino-1.8.0 ~/opt/arduino
В каталоге с IDE можно запустить install.sh, чтобы он создал красивые икноки для вашего окружения рабочего стола. Я пользуюсь i3, поэтому никаких красивых иконочек мне не нужно.
После запуска IDE в Tools → Port выбираем /dev/ttyACM0. Затем жмем Files → Examples → Basics → Blink. Откроется следующий код:
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
Используемый в Arduino язык программирования, как не сложно заметить, является диалектом C/C++. Эта программа просто мигает встроенным в Arduino светодиодом. Вы тут все программисты, поэтому разжевывать, как именно работает код, полагаю, не требуется. Жмем Upload (Ctr+U) и… я лично получил такую ошибку:
libtinfo.so.5: cannot open shared object file: No such
file or directory
Лечится так:
sudo pacman -S ncurses
sudo ln -s /usr/lib/libncursesw.so /usr/lib/libtinfo.so.5
Если все было сделано правильно, теперь после нажатия Ctr+U Arduino начнет мигать желтым светодиодом с подписью L. Бывает так, что программа Blink уже была залита в Arduino заранее. В этом случае попробуйте изменить в коде частоту мигания и перезалить программу.
Работа без IDE
Arduino IDE довольно убога, как текстовый редактор, плюс я заметил в ней неприятные глюки при сохранении файлов. Мне, знаете ли, нравится писать код в Vim. Вам может нравиться Sublime Text или другой текстовый редактор. Так давайте же разберемся, как программировать Arduino в текстовом редакторе или IDE по вкусу.
Говорим и заодно сразу прописываем в ~/.bashrc:
export ARDUINODIR=/home/eax/opt/arduino
Далее нам понадобится файл arduino.mk, который доступен здесь. Однако не спешите его качать. Мне этот файл пришлось во многих местах подправить, чтобы он заработал с последней Arduino IDE. Детали скучны, поэтому я не буду вас ими грузить. Просто берите пропатченную мною версию. Если вам вдруг интересно, что именно я поменял, измененные строчки помечены вот так:
Создаем файл Makefile такого содержания:
TARGET := main SOURCES := main.cpp BOARD := uno
include arduino.mk
… а также main.cpp:
#include
/* Номера пинов, к которым подключены аноды RGB-светодиода */
const int RLED = 9;
const int GLED = 11;
const int BLED = 10;
/* Номера пинов, к которым подключены кнопки */
const int RBUTTON = 2;
const int GBUTTON = 4;
const int BBUTTON = 6;
/* Номер аналогового входа, подключенного к фоторезистору */
const int LIGHT = 0;
/* Как часто считывать состояния кнопок, в мс */
const int DELAY = 10;
/* Как часто выводить уровень освещенности, в мс */
const int LIGHT_REPORT_PERIOD = 1000;
/* Текущая яркость RGB-светодиода */
int rval = 120;
int gval = 0;
int bval = 250;/* * Была ли отпущена соответствующая кнопка. * * Эти переменные нужны, чтобы одно нажатие кнопки * не считалось за несколько.
*/
bool rbutton_released = true;
bool gbutton_released = true;
bool bbutton_released = true;
/* Как долго не выводили уровень освещенности, в мс */
int silent_time = 0;
void setup()
{
pinMode(RLED, OUTPUT);
pinMode(GLED, OUTPUT);
pinMode(BLED, OUTPUT);
pinMode(RBUTTON, INPUT);
pinMode(GBUTTON, INPUT);
pinMode(BBUTTON, INPUT);
Serial.begin(9600);
}
void loop()
{
/* Обработка нажатия первой кнопки */
if(digitalRead(RBUTTON) == HIGH)
{
if(rbutton_released)
{
rbutton_released = false;
rval += 10;
if(rval > 255)
rval = 0;
Serial.println(“rval = ” + String(rval));
}
} else
rbutton_released = true;
/* То же самое для второй кнопки */
if(digitalRead(GBUTTON) == HIGH)
{
if(gbutton_released)
{
gbutton_released = false;
gval += 10;
if(gval > 255)
gval = 0;
Serial.println(“gval = ” + String(gval));
}
} else
gbutton_released = true;
/* То же самое для третьей кнопки */
if(digitalRead(BBUTTON) == HIGH)
{
if(bbutton_released)
{
bbutton_released = false;
bval += 10;
if(bval > 255)
bval = 0;
Serial.println(“bval = ” + String(bval));
}
} else
bbutton_released = true;
/* Задаем уровень ШИМ-сигнала на анодах RGB-светодиода */
analogWrite(RLED, rval);
analogWrite(GLED, gval);
analogWrite(BLED, bval);
/* Небольшая задержка */
delay(DELAY);
silent_time += DELAY;
/* Выводим освещенность, если долго ее не выводили */
if(silent_time >= LIGHT_REPORT_PERIOD)
{
int light_val = analogRead(LIGHT);
Serial.println(“light_val = ” + String(light_val));
silent_time = 0;
}
}
Как видите, программа стала сложнее. Этот код мы разберем подробнее чуть ниже. Если хотите, можете пока что оставить код программы Blink. Главное — не забудьте добавить в начале строчку:
Для загрузки программы говорим make upload. Для просмотра сообщений от Arduino говорим make monitor. В последнем случае будет использован screen. Как альтернативный вариант, можно воспользоваться программой minicom:
sudo pacman -S minicom
minicom -D /dev/ttyACM0 -b 9600
Если подзабыли количество бод, указанное в программе, можно сказать:
Теперь, когда с настройкой рабочего окружения покончено, давайте разберемся, как работает приведенная программа.
Немного матчасти и разбор кода
Приведенная программа управляет RGB-светодиодом, а также считывает данные с трех кнопок и фоторезистора:
RGB-светодиод — это по сути три светодиода в одном корпусе с общим катодом или анодом, которому соответствует наиболее длинная ножка из четырех.
У меня был RGB-светодиод с общим катодом, то есть самую длинную ножку я подключал к минусу. Такой светодиод интересен тем, что его цвет со временем можно менять. Как видите, на фото светодиод светится фиолетово-розовым цветом.
Если у вас нет RGB-светодиода, вы можете с тем же успехом использовать три отдельных светодиода.
Фоторезистор представляет собой резистор, чье сопротивление меняется в зависимости от того, как много света на него падает.
Тот, что есть у меня, имеет сопротивление около 400 Ом при ярком свете и более 2 МОм (максимум, который может померить мой мультиметр) в полной темноте.
Используя его в качестве одного из резисторов в делителе напряжения, можно оценить яркость света вокруг. Если у вас нет фоторезистора, можете с тем же успехом подключить обычный потенциометр.
У Arduino Uno есть 14 цифровых пинов, 6 аналоговых входов, а также пины с 5 В напряжения, 3.3 В и землей. В программе Blink мы использовали 13-ый пин со встроенным светодиодом.
Подавая на него высокое и низкое напряжение, можно заставить светодиод мигать. Также цифровые пины позволяют считывать напряжение. В приведенной выше программе пины 2, 4 и 6 используются для чтения состояния кнопок.
Пока это все не сильно отличается от уже знакомых нам GPIO пинов в Raspberry Pi.
Часть цифровых пинов помечены тильдой (~). У меня таких пинов 6, с номерами 3, 5, 6, 9, 10 и 11. Это означает, что пин может подавать ШИМ-сигнал (Широтно-Импульсная Модуляция или PWM, Pulse-Width Modulation). В данном контексте ШИМ всего лишь означает, что на пин будет подаваться сигнал с заданным коэффициентом заполнения.
В Arduino коэффициент заполнения задается от 0 до 255. Например, если указать 127, то половину времени будет подаваться высокий сигнал, а половину — низкий. Если же указать 0, то сигнал будет все время низким. Частота ШИМ-сигнала в Arduino разная на разных пинах и отличается от модели к модели.
В Arduino Uno частота составляет примерно 980 Гц на пинах 5 и 6, и примерно 490 Гц на остальных ШИМ-пинах.
ШИМ позволяет аппроксимировать подачу напряжения между 0 и 5 В. В приведенной программе нажатия на кнопки увеличивают ШИМ-сигнал на соответствующих анодах RGB-светодиода. В результате тремя кнопками регулируется яркость и цвет светодиода.
Наконец, аналоговые входы предназначены для более точного считывания напряжения, чем просто «высокий сигнал» и «низкий сигнал». В приведенном примере аналоговый вход A0 подключен к делителю напряжения из фоторезистора и резистора сопротивлением 10 кОм.
Вызов analogRead(LIGHT) возвращает значение около 800 при ярком свете, 125 при свете от настольной лампы, 0 в полной темноте и около 960, если посветить на фоторезистор лазерной указкой. В целом диапазон возможных возвращаемых значений лежит от 0 до 1023.
Плюс к этому, аналоговые входы можно использовать точно так же, как и цифровые пины (без ШИМ), например:
pinMode(A1, OUTPUT);
digitalWrite(A1, HIGH);
Fun fact! Фактически, теперь вы знаете, как сделать лазерную сигнализацию.
Также с помощью светодиодов или ИК-светодиодов и фоторезистора можно сделать датчик движения (бесконтактный выключатель?) вроде того, что используется в сушилках для рук.
Также помимо фоторезистора можно использовать фототранзистор, представляющий собой обычный транзистор, ток базы которого определяется интенсивностью света. Если желаете, можете реализовать эти проекты в качестве домашнего задания.
Заключение
С Arduino мне понравилось работать намного больше, чем с Raspberry Pi. У последней, насколько мне известно, аналоговых входов нет совсем, да и с ШИМ все как-то не слишком удобно.
Плюс Arduino Uno — это всего лишь микроконтроллер ATmega328P, на котором нет этой бесполезной операционной системы и который при желании можно извлечь и впаять прямо в изготовленную по ЛУТ плату.
В общем, для занятия электроникой это устройство куда интереснее.
Исходники к этой заметке вы найдете на GitHub. Как обычно, буду рад вашим вопросам и дополнениям.
Дополнение: Как собрать Arduino прямо на макетной плате
Источник: https://eax.me/arduino/
Урок 1. Мигающий светодиод на Arduino
Добрый вечер юные познаватели микроконтроллера Arduino, сегодня мы с вами начнем изучать основы и азы Arduino и поймем принцип его работы. Сегодняшний урок посвящен такому элементу как светодиод и работы c микроконтроллером Arduino. Попросту говоря,
Светодиод — это полупроводниковый прибор, трансформирующий электроток в видимое свечение.
И на основе свечения светодиода мы будем работать и рассматривать основу программирования Arduino. Перейдем непосредственно к практике Для начала нам нужно приготовить необходимый набор предметов для работы!
Для начала работы нам понадобятся такие компоненты
- плата Arduino
- Breadboard (макетная плата для удобного подключения приборов к Arduino)
- Провода
- светодиод
- резистор
Также вам потребуется программа Arduino IDE, которую можно скачать с официального сайта Arduino.
Данные комплектующие можно приобрести на официальном сайте или в интернет-магазине AliExpress или Амперка.
Спросите вы, что такое Breadboard ?
Breadboard– макетная (монтажная) беспаечная плата. Breadboard представляет из себя сетку из гнезд, которые обычно соединяются так:
Далее, когда мы приготовили все компоненты к работе и установили программу на ПК , нам следует правильно их подключить . Подключать нужно очень внимательно, чтобы все компоненты остались целыми и невредимыми.
Схема подключения светодиода к Arduino
Не забудьте проверить “+” и “-” у светодиода. Минус у светодиода можно отличить двумя способами :
- У “минуса” на светодиоде по стандарту ножка вывода длиннее чем у плюса.
- Если вы внимательно всмотритесь в светодиод, то можете увидеть своеобразный флажок, так вот, где флажок там и “минус” светодиода.
После правильного подключения перейдем к этапу программирования
/*
Зажигаем светодиод на одну секунду, затем выключаем его на
одну секунду в цикле.
*/
int led = 8;
/*объявление переменной целого типа, содержащей номер порта к которому мы подключили провод */
void setup() {
/* Инициализируем объявление используемого порта вход/выход в режиме выхода.
*/
pinMode(led, OUTPUT);
}
void loop() {
digitalWrite(led, HIGH); // зажигаем светодиод
delay(1000); // ждем секунду
digitalWrite(led, LOW); // выключаем светодиод
delay(1000); // ждем секунду
}
Код нужно всего лишь скопировать и вставить, тут и ребенок справится. Наша цель понять и разобраться в том, что мы внесли в Arduino.
Перейдем к пояснению нашего скетча (кода)
С начала в нашем скетче мы объявили переменную int led = 8; .
Мы таким образом заявили, что хотим иметь ячейку памяти, к которой будем обращаться по имени led и изначально, при старте Arduino, в ней должно лежать значение 8 пина.
Перед именем переменной в определении указывается тип данных для этой переменной. В нашем случае — это int , что означает «целое число» (int — сокращение от английского «integer»: целочисленный).
Процедура setup выполняется один раз при запуске микроконтроллера. Обычно она используется для конфигурации портов микроконтроллера и других настроек. В нашем случае мы указали, что наш светодиод на 8 выходе . “pinMode(led, OUTPUT);” Хотелось бы сказать, что Arduino выполняет с начала действие setup , а далее выполняет действие другой процедуры, про которую мы сейчас поговорим.
После выполнения setup запускается процедура loop, которая выполняется в бесконечном цикле . Именно этим мы пользуемся в данном примере, чтобы маячок мигал постоянно.
Процедуры setup и loop должны присутствовать в любой программе (скетче), даже если вам не нужно ничего выполнять в них — пусть они будут пустые, просто не пишите ничего между фигурными скобками. Запомните, что каждой открывающей фигурной скобке { всегда соответствует закрывающая } .
Они обозначают границы некого логически завершенного фрагмента кода. Следите за вложенностью фигурных скобок. Для этого удобно после каждой открывающей скобки увеличивать отступ на каждой новой строке на один символ табуляции. Обращайте внимание на ; в концах строк. Не стирайте их там, где они есть, и не добавляйте лишних.
Вскоре вы будете понимать, где они нужны, а где нет.
Функция digitalWrite(pin, value) не возвращает никакого значения и принимает два параметра: pin — номер цифрового порта, на который мы отправляем сигнал value — значение, которое мы отправляем на порт.
Для цифровых портов значением может быть HIGH (высокое, единица) или LOW (низкое, ноль) Если в качестве второго параметра вы передадите функции digitalWrite значение, отличное от HIGH , LOW, 1 или 0, компилятор может не выдать ошибку, но считать, что передано HIGH. Будьте внимательны Обратите внимание, что использованные нами константы: INPUT, OUTPUT, LOW, HIGH, пишутся заглавными буквами, иначе компилятор их не распознает и выдаст ошибку. Когда ключевое слово распознано, оно подсвечивается синим цветом в Arduino IDE
Задачи для самостоятельного решения, для укрепления материала
1) Измените скетч так, чтобы светодиод светился 3 секунды, а пауза между свечением была 0.5 секунды.
2) Измените скетч так, чтобы светодиод ,при включении Arduino, горел непрерывно 4 секунды (подсказка: сделайте это с помощью процедуры setup ), а потом продолжал мигать в интервале, который мы должны были задать в первом задании .
Источник: http://helpduino.ru/svetodiod.html
Arduino и переполнение счетчиков – мигаем светодиодом без delay
Новички, которые начинают изучать программирование микроконтроллеров Arduino (язык программирования Ардуино — Wiring, очень упрощенная версия C++), скорее всего, видели примеры, как помигать встроенным в микроконтроллер светодиодом.
Обычно, в этих примерах используется функция delay(), которая как бы “замораживает” микроконтроллер на время своего выполнения. И если Arduino должен обрабатывать не только задержку при мигании светодиодом (например с задержкой в 5 сек.
), а еще и нажатие на кнопку, то в момент выполнения функции delay() микроконтроллер может попросту “не заметить” нажатие на кнопку. Поскольку, обычно, в скетчах для Ардуино обрабатывается более 2 событий, использовать функцию delay() весьма не желательно.
Чем и как правильно заменить delay, рассказано ниже.
Все примеры, опубликованные в этой статье протестированы на микроконтроллере (далее МК) Arduino Mega 2560.
Arduino – мигаем светодиодом
// Включение светодиода на 1 секунду и отключение на тот же интервал в цикле void setup() { // Инициализируем цифровой вход/выход в режиме выхода. // Выход 13 на большинстве плат Arduino подключен к светодиоду на плате. pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); // Подаем напряжение на светодиод delay(1000); // Ожидаем 1 секунду digitalWrite(13, LOW); // Отключаем напряжение со светодиода delay(1000); // Ожидаем 1 секунду }
Этот пример служит только для быстрого старта, чтобы бы можно было сразу увидеть результат программирования микроконтроллера, и не сильно вникать в тонкости программирования.
Проблемы начинаются тогда, когда от Arduino требуется выполнить несколько действий одновременно – функция delay() полностью “зависает” микроконтроллер на время своего выполнения, не позволяя параллельно обрабатывать, например, нажатия кнопок, показания датчиков и т.п.
Многозадачность Arduino
Для того, чтобы можно было выполнять несколько действий на Ардуино одновременно – следует воспользоваться таймерами. Пример вывода информации в Serial Monitor и параллельного мигания светодиодом без использования функции delay():
// http://petrenco.com/arduino.php #define LED_MK_PIN 13 // Номер выхода, к которому подключен внутренний светодиод #define LED_MK_INTERVAL 1000UL // Интервал между включениея и выключения LED #define SERIAL_SPEED 115200 // Скорость работы порта Serial – то же число должно быть выбрано и в мониторе порта #define SERIAL_INTERVAL 3000UL // Периодичность вывода времени в Serial (1 cсекунда) void setup() { pinMode(LED_MK_PIN, OUTPUT); Serial.begin(SERIAL_SPEED); } void loop() { // Включение/отключение светодиода static unsigned long led_mk_timer = 0; if (millis() – led_mk_timer > LED_MK_INTERVAL) { led_mk_timer = millis(); // digitalWrite(LED_MK_PIN, !digitalRead(LED_MK_PIN)); } // Вывод данных в Serial с заданным интервалом static unsigned long serial_timer = 0; static byte test_value_1 = 5; static byte test_value_2 = random(0, 255); static byte test_value_result; if (millis() – serial_timer > SERIAL_INTERVAL) { serial_timer = millis(); test_value_2 = random(0, 255); test_value_result = test_value_1 – test_value_2; Serial.print(“Current time:”); Serial.println(millis()); Serial.print(“test_value_1: “); Serial.print(test_value_1); Serial.print(“; test_value_2: “); Serial.print(test_value_2); Serial.print(“; test_value_result (test_value_1 – test_value_2): “); Serial.print(test_value_result); Serial.println(); Serial.println(); }
}
На самом деле, МК не имеет функций многозадачности, и не может параллельно/одновременно выполнять несколько различных действий.
Но благодаря тому, что каждое последовательное действие в примере выше происходит очень быстро, за несколько микросекунд (1 секунда = 1 000 000 микросекунд), с использованием таймеров можно добиться того, что будет казаться, как будто бы Ардуино выполняет несколько различных операций одновременно.
Если внимательно изучить код, то на первый взгляд может сложиться впечатление, что данные будут выводиться только в течении 4 294 967 295 миллисекунд, что приблизительно равно 50 дням. Ведь самый вместительный тип переменной unsigned long ограничен именно этим числом.
При достижении в функции mills() значения 4 294 967 295 (в ней так же используется тип unsigned long), на следующем шаге её значение будет сброшено в 0, и далее снова начнет увеличиваться от нуля.
Если применить всем известные математические правила к случаю, когда в serial_timer будет, например, через 50 дней записано значение 4 294 967 000, то после сброса mills() в ноль, строка if (millis() – serial_timer > SERIAL_INTERVAL) (например 5000 – 4 294 967 000 > 3000) может никогда не стать верной, соответственно, через 50 дней сообщения в Serial Monitor выводиться более не будут. Но то что кажется – не всегда верно, и вот почему.
Специально для того, чтобы объяснить, как будет работать таймер после сброса значения в ноль, в скрипте добавлены переменные: test_value_1, test_value_2, test_value_result.
Все они объявлены, как тип byte, в котором могут храниться только положительные числа от 0 до 255 (byte является всегда только unsigned, поэтому писать unsigned byte будет ошибкой).
Переменные объявлены как byte исключительно для облегчения восприятия, но то же самое будет происходить и с типом unsigned long. Теперь рассмотрим вывод данных и разберем, как так может получиться, что 5 минус 19 будет равно 242?
Current time:3001 test_value_1: 5; test_value_2: 19; test_value_result (test_value_1 – test_value_2): 242 Current time:6002 test_value_1: 5; test_value_2: 158; test_value_result (test_value_1 – test_value_2): 103 Current time:9003 test_value_1: 5; test_value_2: 38; test_value_result (test_value_1 – test_value_2): 223
Такая “странная”, на первый взгляд математика происходит из за того, что в переменной не могут содержаться отрицательные значения из-за её типа.
МК считает так (образно): поскольку 19 более 5, то от числа пять сначала отнимается пятерка, её же отнимаем от 19: 5 – 5 = 0, в остатке от 19 остается 14 (19 – 5 = 14).
Далее, от 0 отнимается единица, что дает максимальное значение этого типа переменной (byte) 255, в остатке остается 13. Отняв 13 от 255 (255 – 13) и получается 242!
Теперь разберем следующий пример, вычтем из трех восемь (3 – 8). Цифру восемь раскладываем на 3 + 1 + 4. Теперь считаем: 3 – 3 = 0, 0 – 1 = 255, 255 – 4 = 251.
Работа с таймерами, приведена для примера, существуют более красивые, в плане читаемости кода, решения: классы, функции, сторонние библиотеки и др., но принципы их работы основываются на том, что все переменные, используемые в таймерах, объявляются как unsigned.
Если после прочтения статьи останется что-то не понятно – спрашивайте в комментариях и смотрите статьи по ссылкам ниже.
Источник: https://petrenco.com/arduino.php?txt=193
Мигающий светодиод на плате Arduino
Всем привет! В данной статье мы сделаем то, о чем мечтает каждый начинающий ардуинщик — помигаем светодиодом! Сейчас для многих из вас это покажется банальностью и сущим пустяком, вы будете смеяться над теми, кто повторяет это вновь и вновь. А теперь вспомните, как еще несколько лет назад вы сами пробовали это делать?
Возможно, получалось не с первого раза: неправильная настройка платы в среде разработки, поиск нужного резистора, установка драйверов на плату и т. д. Но когда светодиод начинал мигать… О, да! И если и сравнивать людей, которые жили в Советском Союзе с нынешним поколением, то у наших было самое лучше детство — они мигали светодиодом
Источник: https://Voltiq.ru/blinking-led-arduino/