3.2.md
ИСПОЛЬЗОВАНИЕ ТАЙМЕРОВ ДЛЯ УПРАВЛЕНИЯ РАБОТОЙ ПРОГРАММОЙ
ТАЙМЕРЫ — устройства микроконтроллеров, предназначенные для измерения событий или временных промежутков времени. Это широко используемые устройства. В нашем контроллере встроено три таймера, но каждый из них обладает еще 4 каналами измерения.
В микроконтроллерах таймеры считают количество сигналов (импульсов), поступающих на их входы. Как правило происходит подсчет импульсов от внутренних генераторов сигналов. Хотя можно и считать внешние сигналы. Так как внутренние сигналы идут через строго определенные промежутки времени, то мы и можем получается точно измерить временной промежуток времени умножив время между сигналами на их количество.
НАПОМИНАНИЕ: при использовании внешнего питания сначала подключаем внешнее питание, а потом USB-кабель для связи или программирования. При выключении все делаем наоборот: сначала вытаскиваем USB-кабель, а затем внешнее питание. Несоблюдение данного правила может привести к выходу из строя платы Рудирон.
ИСПОЛЬЗОВАНИЕ ТАЙМЕРОВ В ПРОГРАММАХ
Наша первая программа заставляла светиться непрерывно два светодиода L1 и L2. Немного скучно. Свет не сильный. Как фонарик не сможем использовать. Давайте сделаем интереснее процесс свечения светодиодов. Заставим их мигать.
Мы научились управлять выводом напряжения на порт контроллера. Написали программу подачи напряжения 3.3 вольта на выводы порта, к которым подключены светодиоды L1 и L2.
Логично, что если мы хотим заставить мигать светодиоды, то нам необходимо сначала подать 3.3 вольта на выход порта. Потом подождать, например 1 секунду и подать 0 вольт на вывод порта, чтобы светодиод погас. Затем еще подождать 1 секунду и заново повторять программу.
Что значит подождать 1 секунду для микроконтроллера – измерить промежуток времени в 1 секунду. А точнее подсчитать количество импульсов при их времени появления каждую одну миллисекунду. Когда будет подсчитано 1000 импульсов, значит прошла одна секунда.
Для такой реализации мы настраиваем внутренние устройства контроллера так, чтобы на один из таймеров поступали импульсы длительностью 1 миллисекунды и когда таймер отсчитает 1000 таких импульсов сообщить программе об этом событии.
Так как внутреннее устройство микроконтроллера сложное для новичков, то мы создали команды за вас, чтобы вы могли сконцентрироваться на управлении контроллером с минимум знаний.
Познакомимся с двумя командами (функциями) паузы (измерения):
- delay(200) – для счета необходимо указать количество миллисекунд;
- delayMicroseconds(10) – для счета указываем количество микросекунд.
Пауза означает, что пока таймер считает программа не выполняется. Устройство замирает и ждет, когда таймер досчитает до заданного числа и только потом программа продолжит выполняться дальше.
Напишем программу, которая будет каждые 100 микросекунд сначала будет подавать напряжение 3.3 вольта на светодиоды, а потом на 100 микросекунд будет подавать напряжение 0 вольта на светодиоды.
#i#include "Arduino.h" void setup()
{
pinMode(LED_BUILTIN_1, OUTPUT); pinMode(LED_BUILTIN_2, OUTPUT);
}
void loop()
{
digitalWrite(LED_BUILTIN_1, true); digitalWrite(LED_BUILTIN_2, true);
delayMicroseconds(100);
digitalWrite(LED_BUILTIN_1, false); digitalWrite(LED_BUILTIN_2, false);
delayMicroseconds(100);
}
Рассмотрим поближе программу. Блок setup у нас не изменился. В нем мы установили режимы работы двух выводов контроллера на вывод.
Самое интересное это блок loop(). В нем мы сначала «включаем светодиоды»
digitalWrite(LED_BUILTIN_1, true);
digitalWrite(LED_BUILTIN_2, true);
и светодиоды начинают светиться. Затем мы вызываем таймер и он отсчитывает 100 микросекунд, контроллер ничего не делает – светодиоды светятся. Еще через 100 микросекунд у нас выполняются команды отключения светодиодов
digitalWrite(LED_BUILTIN_1, false);
digitalWrite(LED_BUILTIN_2, false);
Почему они не мигают, а постоянно светятся?!
Сначала разберемся в блоке loop()! Loop переводится как петля. Это значит, что после паузы мы перейдем на начало данного блока и заново начнем выполнять программу. И так будет длиться бесконечно долго пока подается питание на контроллер. В таких случаях говорят, что контроллер работает в бесконечном цикле.
Итак, программа начинается заново! Снова включаем светодиоды (светятся), пауза 100 микросекунд, выключение светодиодов (не светятся), пауза 100 микросекунд, снова начало программы и т.д. Это и есть наш бесконечный цикл.
Вы написали правильную программу, но помните мы говорили, что, создавая устройства на микроконтроллере мы не только программисты, но и электронщики, физики и т.д. Так вот человеческий глаз не может увидеть события длительностью 100 микросекунд. Поэтому кажется, что светодиоды не мигают. Чтобы наш глаз мог увидеть отдельные вспышки света от светодиодов необходимо, чтобы длительность была меньше 10-15 событий в секунду, на пример 500 миллисекунд (0.5 раз в секунду). Для этого используем паузу за счет подсчета миллисекунд, а не микросекунд. Для этого используем команду delay(), которая отсчитывает миллисекунды:
#i#include "Arduino.h"
void setup()
{
pinMode(LED_BUILTIN_1, OUTPUT); pinMode(LED_BUILTIN_2, OUTPUT);
}
void loop()
{
digitalWrite(LED_BUILTIN_1, true); digitalWrite(LED_BUILTIN_2, true);
delay(500);
digitalWrite(LED_BUILTIN_1, false);
digitalWrite(LED_BUILTIN_2, false);
delay(500);
}
Собрав такой проект и загрузив его, в контроллер мы увидим, как два светодиода L1 и L2 мигают одновременно два раза в секунду. В действительности сначала включается светодиод L1 (команда digitalWrite(LED_BUILTIN_1, true) идет первой), а затем L2, но так как микроконтроллер выполняет каждую команду меньше чем за микросекунду, то мы не видим разницы во времени их включения. Для нас они одновременно «загораются» и «гаснут».
Если посмотрим текст программы, то увидите текст, выделенный зеленым.
Это комментарии - текст, который не является частью программы. При сборке проекта любой текст после // будет игнорироваться. Тогда зачем же комментарии?!
Программы могут достигать сотен и тысяч строк текста. И можно очень быстро забыть зачем ты писал такой набор команд. Комментарии — подсказки самому себе (или программисту, изучающему чужой код программы). Вдруг тебя попросят изменить программу через год?! Чтобы быстро вспомнить, что происходит в программе мы и воспользуемся комментариями. Комментарии быстро тебе подскажут с какой целью ты писал данные строчки программы.
Комментарии пишут все программисты. Начинающие могут комментировать изначально каждую строку программы. Но опытные программисты стремятся комментировать целые части программы, так как комментарии каждой строчки избыточны, утомительны и требуют много времени на их написание. Не забывайте, что скорость написания программы тоже важна.
Рассмотрим программу, которая попеременно заставляет светиться встроенные светодиоды LED1 и LED2. Каждые 500 миллисекунд происходит смена светящегося светодиода. Первым светится LED2 (LED1 не светится) потом LED1 (LED2 не светится) и повторяются все действия заново.
#i#include "Arduino.h"
void setup()
{
pinMode(LED_BUILTIN_1, OUTPUT); // устанавливаем порт на вывод
pinMode(LED_BUILTIN_2, OUTPUT); // устанавливаем порт на вывод
}
void loop()
{
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
digitalWrite(LED_BUILTIN_2, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED1) 3.3 Вольта - светится
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED2) 0 Вольт – не светится
delay(500);
}
Программа отличается от первоначальной тем, что мы поменяли местами подачу напряжения (логическая 1) на вывод контроллера, к которому подключен светодиод LED1. На техническом языке мы разсинхронизировали работу светодиодов LED1 и LED2.
Усложним задачу. Пуская светодиод LED2 «мигнет» 5 раз, а светодиод LED1 в это время не светится. Потом картина поменяется местами – будем «мигать» светодиод LED1, а LED2 не будет светиться.
#i#include "Arduino.h" void setup()
{
pinMode(LED_BUILTIN_1, OUTPUT); // устанавливаем порт на вывод
pinMode(LED_BUILTIN_2, OUTPUT); // устанавливаем порт на вывод
}
void loop()
{ // подаем лог 0 на все светодиоды, чтобы они гарантировано не светились
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт - светится
// пять раз подаем на светодиод LED2 лог 1 задержка 500 миллисекунд и подача лог 0
digitalWrite(LED_BUILTIN_2, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт – не светится
delay(500);
digitalWrite(LED_BUILTIN_2, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольта – не светится
delay(500);
digitalWrite(LED_BUILTIN_2, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт – не светится
digitalWrite(LED_BUILTIN_2, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт – не светится
delay(500);
digitalWrite(LED_BUILTIN_2, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт – не светится
delay(500);
// пять раз подаем на светодиод LED1 лог 1 задержка 500 миллисекунд и подача лог 0
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED2) 3.3 Вольта – светится
delay(500);
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED2) 3.3 Вольта – светится
delay(500);
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED2) 3.3 Вольта – светится
delay(500);
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED2) 3.3 Вольт – светится
delay(500);
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
Получилась довольно-таки объемная программа для таких простых действий. Представьте какой размер получился бы у текста программы если надо было мигать не 5 раз, а например 1000 раз каждым светодиодом?!
Для уменьшения текста программы используем конструкцию языка С++, называемую цикл.
Детально познакомиться с циклами вы можете в справочной литературе по языку С++ или учебниках, которых выпущено огромное количество. Наши уроки направлены на изучение программного взаимодействия с контроллером. Подразумевается, что вы знаете основы языка С++.
Текст новой программы:
#i#include "Arduino.h void setup()
{
pinMode(LED_BUILTIN_1, OUTPUT); // устанавливаем порт на вывод
pinMode(LED_BUILTIN_2, OUTPUT); // устанавливаем порт на вывод
}
void loop()
{ // подаем лог 0 на все светодиоды, чтобы они гарантировано не светились
digitalWrite(LED_BUILTIN_1, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт - не светится
// пять раз подаем на светодиод LED2 лог 1 задержка 500 миллисекунд и подача лог 0
for(int i=1; i<6; i++) // описываем цикл с созданием переменной i
{
digitalWrite(LED_BUILTIN_2, true); // подаем на выход
(светодиод LED2) 3.3 Вольта - светится
delay(500);
digitalWrite(LED_BUILTIN_2, false); // подаем на выход (светодиод LED1) 0 Вольт – не светится
delay(500);
}
// пять раз подаем на светодиод LED1 лог 1 задержка 500 миллисекунд и подача лог 0
for(int i=0; i<5; i++) // описываем цикл с созданием переменной i
{
digitalWrite(LED_BUILTIN_1, false);// подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
digitalWrite(LED_BUILTIN_1, true); // подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
}
}
Текст программы сократился и стал более читабельным. Теперь мы можем организовать любое количество «миганий» светодиодами изменив в тексте всего лишь количество нужных повторений. Мы использовали цикл с предусловием — сначала анализируется условие выполнения цикла, а затем само действие цикла. Для создания цикла используем конструкцию:
for(int i=1; i<6; i++) // описываем цикл с созданием переменной i
{
Код цикла
}
int i — резервирование ячейки памяти (создается переменная с именем i) в контроллере для хранения количества действий, произведенных циклом. Иногда такую переменную называют счетчиком цикла. Начальное значение устанавливаем в 1.
i < 6 — условие при котором цикл перестанет выполнять действия (программа «выйдет из цикла»). Программа продолжит свое исполнение со следующей команды, идущей после цикла.
i++ — действия со счетчиком цикла после однократного выполнения кода цикла. В данном случае увеличение на единицу.
Между двойными фигурными скобками размещаем команды, которые будут выполняться 5 раз.
{
digitalWrite(LED_BUILTIN_1, false);// подаем на выход (светодиод LED1) 0 Вольт - не светится
delay(500);
digitalWrite(LED_BUILTIN_1, true);// подаем на выход (светодиод LED2) 3.3 Вольта - светится
delay(500);
}
В нашей программе два цикла. Один цикл для светодиода LED1 и второй для LED2. Они имеют небольшое различие. Оба цикла исполняют код 5 раз. Ноу второго цикла i=0, а условие i<5. Такая форма записи предпочтительнее. Такое написание сразу показывает количество действий цикла – 5. Так же при работе с такими структурами данных как массивы мы будем обращаться к элементам массива через индекс (номер элемента массива относительно начала самого массива), который всегда начинается с 0!
РЕЗУЛЬТАТ ВЫПОЛНЕНИЯ
Загруженная программа последовательно управляет включением и выключением встроенных светодиодов с интервалом в пол секунды. Оба варианта программы работают одинаково.
ПРИМЕЧАНИЕ Если Вы хотите выполнять программу при последующих включениях «Рудирона», то не забудьте переставить перемычку из положения «Режим программирования» обратно в «Режим выполнения». Если этого не сделать, то программа при включении «Рудирона» выполняться не будет.
Все только еще начинается. Вы начинаете набирать опыт и процесс создания своих устройств будет интересным и увлекательным.
ВЫВОДЫ
Мы научились управлять встроенными светодиодами на плате, разобрали как можно оптимизировать выполнение своих программ, используя циклы. Убедились, что оба варианта программы работают одинаково.
СПИСОК КОНТРОЛЬНЫХ ВОПРОСОВ
1 Сколько доступных для управления пользователем светодиодов есть на плате? 2 Что обозначают названия LED_BUILTIN_1 и LED_BUILTIN_2? 3 Что будет результатом выполнения нашей программы? 4 В чем отличие написанных вариантов программы? 5 Как выполняется цикл FOR в программе? 6 Для чего необходима команда digitalWrite? 7 Для чего необходима команда delay? 8 Для чего необходима команда pinMode? 9 В чем разница между командами delayMicroseconds и delay? 10 Почему в циклах рекомендуется отсчет начинать с «0»?
СПИСОК ДОПОЛНИТЕЛЬНЫХ ЗАДАЧ
1 Использовать для написания программы среду Arduino IDE. 2 Управлять только левым светодиодом. 3 Управлять только правым светодиодом. 4 Задать другое значение длительности паузы при включении/выключении светодиодов. 5 Одновременное управление двумя светодиодами.
ЭЛЕМЕНТЫ КОНТРОЛЯ И ПРОВЕДЕНИЯ СОРЕВНОВАНИЙ
1 Проведение тестирования по контрольным вопросам. Критерии: кто быстрее и допустил меньшее количество ошибок. 2 Написание одного из вариантов (управление двумя светодиодами с разными интервалами включения) программы и загрузка этого варианта в контроллер. Критерии: Правильно написанная программа согласно выданного задания, работающий контроллер и наименьшее количество времени затраченного на эти работы.