Самостоятельная работа #2: подключить 2-3 дополнительных светодиода, каждый через свой токоограничительный резистор. Добавить код для их переключения, можно также поэкспериментировать с различными световыми эффектами (поочередное или параллельное мигание и пр).
2.4 Мигаем светодиодом: широтно-импульсная модуляция
В первой части мы уже рассматривали изменение яркости светодиода с помощью ШИМ - широтно-импульсной модуляции. Там мы использовали таймер NE555, чтобы создать напряжение такого вида:
То же самое легко запрограммировать с помощью контроллера. Напишем программу, которая будет плавно повышать яркость светодиода от нуля до максимума.
int led = 13;
int pwm = 0;
void setup() {
pinMode(led, OUTPUT);
}
void loop() {
for(int i=0; i<1000; i++) {
digitalWrite(led, HIGH);
delayMicroseconds(pwm);
digitalWrite(led, LOW);
delayMicroseconds(100 - pwm);
}
pwm += 1;
if (pwm > 100) pwm = 0;
}
Мы создали глобальную переменную pwm, в которой сохраняется текущее значение уровня заполнения в процентах. Дальше мы включаем “высокое” и “низкое” состояние вывода, в соответствии с этим значением - когда одно значение велико, второе, наоборот, мало. Цикл “for(int i=0; i<1000; i++)” повторяет участок кода 1000 раз - без него светодиод менял бы яркость слишком быстро.
Если загрузить этот код, мы увидим плавно увеличивающий яркость светодиод. Но у вышеприведенного кода есть недостатки. Во-первых, он довольно-таки громоздкий - слишком много строк для переключения только одного вывода. Во-вторых, процессор занят только переключением светодиода, любая другая задача нарушит согласованность временных интервалов. К счастью для нас, разработчики процессора пошли навстречу пользователям, и формирование ШИМ может выполняться автоматически, на аппаратном уровне. Для этого достаточно использовать функцию analogWrite, в качестве параметра указав степень заполнения в виде параметра 0..255.
Например, для установки яркости 50% достаточно написать:
analogWrite(led, 128);
Процессор сам сделает все остальное, и сформирует на выходе нужный сигнал. Наш код в это время может делать что-то другое, например выводить информацию на ЖК-экран. Единственное ограничение - режим ШИМ может работать не на всех выводах, это определяется моделью процессора. Например, для Arduino Uno для ШИМ доступны только номера выводов 3, 5, 6, 9, 10, и 11.
Разумеется, с помощью ШИМ управлять можно не только яркостью одного светодиода, но и более мощной нагрузкой постоянного тока (лампа, светодиодная лента и пр), подключив ее через транзистор.
Самостоятельная работа: переписать вышеприведенную программу с использованием analogWrite. Проверить работоспособность, подключив светодиод с резистором к соответствующему выводу.
2.5 Вывод данных через Serial port
В простых случаях можно понять, что делает программа, просто посмотрев на ее текст. Но увы, так бывает далеко не всегда. Более сложные платы, например STM32, имеют специальный разъем для программирования, позволяющий не только загружать программы, но и задавать точки останова, просматривать значения переменных, выполнять программу по шагам. На Arduino такой возможности нет, зато есть возможность вывода данных через “последовательный порт”.
На старых компьютерах были такие порты, называемые COM и LPT. Разумеется, физически отдельного COM-порта на Arduino нет. Его роль играет микросхема FTDI, создающая виртуальный порт при подключении платы по USB.
Еще раз посмотрим в правый нижний угол Arduino IDE.
Отображаемый в углу “COM7” - это и есть тот самый порт, через который Windows “общается” с Arduino. Его несложно использовать в различных программах, например передавать с платы на компьютер значения датчика температуры. Но не менее важная функция - это вывод значений переменных, что позволяет проверить правильность работы программы. Этот процесс называется “отладка” или “debugging”, что переводится как “поиск жучков”. Самые первые компьютеры тоже работали в двоичной системе счисления, но вместо транзисторов, имели механические реле. По легенде, бабочка попала в такое реле, из-за чего контакты перестали замыкаться, и разумеется, программа стала работать неправильно. Много лет прошло, и компьютеры уже давно не механические, а название так и осталось (подробнее можно прочитать в Википедии).
Рассмотрим вывод данных в порт из программы:
void setup() {
// Открыть порт на нужной скорости
Serial.begin(9600);
}
void loop() {
for(int x=0; x< 64; x++) {
// Вывод числа в разных форматах:
Serial.print(x);
Serial.print("\t");
Serial.print(x, DEC);
Serial.print("\t");
Serial.print(x, HEX);
Serial.print("\t");
Serial.println(x, BIN);
delay(200);
}
Serial.println();
}
Рассмотрим код программы подробнее.
Скорость порта. Она задается в функции setup(). Для того, чтобы два устройства обменивались данными между собой, данные должны передаваться на одинаковой скорости. Скорость измеряется в бодах, и может быть различной, например 9600 или 115200 бод. Бод - это количество изменений сигнала в секунду. В нашем случае сигналы могут быть только двоичными, так что скорость в бодах соответствует скорости в битах в секунду. Можно использовать любую скорость, главное чтобы на приёмной и передающей сторонах они были одинаковыми.
Что будет если установить неправильную скорость? Ничего хорошего - система не знает, какой скорость должна быть, так что вместо данных мы получим просто мусор. Последовательный протокол - это одна из самых простых систем передачи данных, и какой-либо защиты или автоматического определения параметров тут нет, пользователь должен настроить все параметры самостоятельно.
Собственно, вывод данных, реализуется с помощью функции Serial.print. Тут все просто, функция посылает данные “как есть”, причем можно послать как текстовую строку, так и значение числовой переменной. Также для удобства чтения можно использовать вариант функции println - она делает так называемый “возврат каретки” (CR, carriage return), устаревший термин, обозначающий перевод курсора на новую строку. Как можно видеть в коде, число можно вывести в разных системах счисления - десятичной, 16-ричной, двоичной. Знак “табуляции” - "\t" вставляет отступ между числами, что также делает чтение более удобным.
Наконец, программа готова, загружаем ее в Arduino. Мы видим как светодиод на плате начинает мигать - Arduino передает данные в порт. Чтобы увидеть их, выбираем в Arduino IDE пункт меню Serial Monitor и видим наши данные.
Для опыта можно попробовать выбрать другую скорость приема - мы увидим, что вместо данных на экране появляются нечитаемые символы.
2.5 Ввод данных: определяем нажатие кнопки
Сложно найти хоть какое-либо электронное устройство, совсем не имеющее кнопок. Кнопки несложно подключить и к Arduino - любой вход микроконтроллера может работать не только как “выход”, но и как “вход”.
Сначала, кнопку надо подключить, как показано на схеме:
На макетной плате это может выглядеть примерно так: