В прошлой статье, посвящённой программированию схем на базе платы Arduino, мы задавали реакцию светодиодов на нажатие кнопки. Сейчас предлагаю продолжить эту тему, немного усложнив. Мы будем использовать то же устройство, которое делали в прошлый раз, состоящее из трёх светодиодов с кнопкой, и платы Aduino UNO.
Схема была такая.
Усложнять мы будем программный код. А также, добавим в эту схему, ради большей наглядности, светодиодное дерево.
Для его создания мы использовали по 5 диодов каждого цвета — красного, зелёного и желтого. К положительным полюсам диодов, мы припаяли по резистору на 220 КОм. Получается, что от каждого диода отходит по 2 провода — плюс и минус.
Плюсы каждой пятёрки диодов одного цвета я вывела на один провод, минусы на другой.
И того получилось шесть проводов.
Три провода питания (красный, жёлтый и зеленый) мы будем подключать к контактам Arduino, остальные три (они у нас чёрные) пойдут на шину заземления, - всё по нашей схеме. Провода от каждого диода мы затянули изолентой, получились ветки светодиодного дерева.
Для поддержки всей структуры использовали трубы (кабель-каналы 20мм в диаметре), соединенные фланцами разработанными и напечатанными на 3D принтере.
В основании центральной трубы мы сделали отверстие, чтобы вывести все шесть проводов.
Дети вырезали цветы из пластичной замши и, - дерево готово.
Теперь давайте разберём программный код.
Мы написали программу, которая задаёт поведение трёх диодов по определённому, циклически повторяющемуся порядку. Таких порядков можно задать множество, а нажатием на кнопку переключаться от одного к другому.
Ниже приводится код программы для трёх диодов и кнопки.
Давайте посмотрим основные моменты.
Номера контактов Arduino, к которым подключены диоды удобно хранить в массиве.
int LEDS[3] = {2, 3, 4}; |
Даже если контакты идут не по порядку, в остальной программе обращение к ним происходит через индексы массива. Это упрощает код и делает возможной задание их параметров в циклах.
Например, в функции setup() происходит инициализация контактов:
for(int i = 0; i < sizeof_LEDS; i++) { pinMode(LEDS[i], OUTPUT); digitalWrite(LEDS[i], LOW); } |
Или в функции loop() задаются значения контактов:
for(int i = 0; i < sizeof_LEDS; i++) { digitalWrite(LEDS[i], current_prog.stadii[current_prog.current_stad][i]); } |
Всё это особенно удобно если диодов не три, а больше.
Программу поведения диодов будет хранить структура PROG.
//структура, хранящая параметры текущей программы для светодиодов struct PROG { /*текущая стадия (индекс) выполняемой программы идет по массиву stadii*/ int current_stad = 0; //максимальное число стадий (циклически повторяются) int max_stad_count = 12; //состояния диодов на каждом шаге выполняемой программы int stadii[12][3] = { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }; //задержка перед выполнением следующего шага |
Как видим, в этой структуре есть массив stadii, описывающий состояние каждого из 3х диодов на каждом шаге в виде нулей и единиц. 0 — диод не горит, 1 — диод горит. Задаётся максимально возможное число шагов (max_stad_count), у нас 12. По достижению 12го шага, или 12го элемента массива, мы начинаем выполнять первый — и так по-кругу. В структуре хранится индекс текущего шага (current_stad), который будет постоянно меняться. А также переменная, задающая временной интервал между шагами в миллисекундах (stad_delay).
Функция check_button_state() каждые 5 миллисекунд вызывается в цикле loop() и проверяет произошло ли нажатие кнопки. Если произошло событие и кнопка была нажата, то мы вызываем функцию, записывающую в текущий объект структуры новые данные, задающие следующую программу поведения диодов.
if( (current_but_state == true) && (but_state == false) ) { //кнопка нажата - значит выполняем следующую программу current_prog_index++; but_state = true; //определяем по индексу текущей программы, какую выполнять if(current_prog_index == 1) set_prog1(); else if(current_prog_index == 2) set_prog2(); else if(current_prog_index == 3) set_prog3(); else { current_prog_index = 1; set_prog1(); } } |
Так что программы поведения расписываются в специальных функциях. У нас это 3 функции — 3 программы (set_prog1(), set_prog2(), set_prog3()). Чтобы задать сценарии поведения диодов, достаточно в этих функциях расставить нули и единицы в нужных местах.
Приводим полный код программы:
/*Программа для трёх диодов и одной кнопки. Содержит несколько сценариев поведения диодов. При нажатии на кнопку сценарии меняются.*/ //массив, содержащий номера контактов int LEDS[3] = {2, 3, 4}; // размер массива LEDS int sizeof_LEDS = 3; /* целочисленная константа с номером контакта от кнопки, этот контакт будет опрашивать состояние кнопки*/ const int BUTTON = 12; /* переменная, хранящяя состояние кнопки, может принимать только значения true или false */ bool but_state = false; //временной интервал, через который опрашивается состояние кнопки int check_but_state_delay = 5; //индекс текущей программы для светодиодов int current_prog_index = 1; //структура, хранящая параметры текущей программы для светодиодов struct PROG { /*текущая стадия (индекс) выполняемой программы идет по массиву stadii*/ int current_stad = 0; //максимальное число стадий (циклически повторяются) int max_stad_count = 12; //состояния диодов на каждом шаге выполняемой программы int stadii[12][3] = { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }; //задержка перед выполнением следующего шага // инициализация всех переменных //функция, в которой циклически повторяются заданные нами действия /*устанавливаем значение кнопок в соответствии со следующим шагом программы*/ /*следующий шаг делаем текущим, предварительно проверив конечный он или нет.*/ //функция проверяет индекс текущего шага программы /*Функция, проверяющая изменилось ли состояние кнопки*/ /*сравниваем с предыдущим состоянием кнопки. Если раньше кнопка была отжата, а теперь нажата, то меняем значение переменной на true для того, чтобы программа среагировала только на факт нажатия*/ /*далее идут функции, которые задают разные программы поведения диодов, записывая их в структуру программ*/ int local_stadii[12][3] = { for (int i = 0; i < 12; i++) current_prog.stad_delay = 500; void set_prog2() int local_stadii[12][3] = { for (int i = 0; i < 12; i++) current_prog.stad_delay = 300; void set_prog3() int local_stadii[12][3] = { for (int i = 0; i < 12; i++) current_prog.stad_delay = 500; |
Заливаем программу в микросхему Arduino и смотрим на результат. Светодиодное дерево включает и выключает диоды в соответствии с заданными программами, а нажатие кнопки переключает эти программы.
Еще что здесь можно сделать интересного — это подключить провода светодиодного дерева не к цифровым, а к аналоговым контактам. Использовать вместо функции digitalWrite(), которая принимает только 2 значения 0 и 1, функцию analogWrite(), принимающую значения от 0 до 255. Таким образом мы сможем не только управлять включением и выключением диодов, но и их яркостью.
Ниже, приводим код программы под аналоговые порты.
/*Программа для трёх диодов и одной кнопки. Содержит несколько сценариев поведения диодов. При нажатии на кнопку сценарии меняются.*/ //массив, содержащий номера контактов int LEDS[3] = {3, 5, 6}; // размер массива LEDS int sizeof_LEDS = 3; /* целочисленная константа с номером контакта от кнопки, этот контакт будет опрашивать состояние кнопки*/ const int BUTTON = 12; /* переменная, хранящяя состояние кнопки, может принимать только значения true или false */ bool but_state = false; //временной интервал, через который опрашивается состояние кнопки int check_but_state_delay = 5; //индекс текущей программы для светодиодов int current_prog_index = 1; //структура, хранящая параметры текущей программы для светодиодов struct PROG { /*текущая стадия (индекс) выполняемой программы идет по массиву stadii*/ int current_stad = 0; //максимальное число стадий (циклически повторяются) int max_stad_count = 15; //состояния диодов на каждом шаге выполняемой программы int stadii[15][3] = { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }; //задержка перед выполнением следующего шага // инициализация всех переменных //функция, в которой циклически повторяются заданные нами действия /*устанавливаем значение кнопок в соответствии со следующим шагом программы*/ /*следующий шаг делаем текущим, предварительно проверив конечный он или нет.*/ //функция проверяет индекс текущего шага программы /*Функция, проверяющая изменилось ли состояние кнопки*/ /*сравниваем с предыдущим состоянием кнопки. Если раньше кнопка была отжата, а теперь нажата, то меняем значение переменной на true для того, чтобы программа среагировала только на факт нажатия*/ /*далее идут функции, которые задают разные программы поведения диодов, записывая их в структуру программ*/ int local_stadii[15][3] = { for (int i = 0; i < 15; i++) current_prog.stad_delay = 500; void set_prog2() int local_stadii[15][3] = { for (int i = 0; i < 15; i++) current_prog.stad_delay = 300; void set_prog3() int local_stadii[15][3] = { for (int i = 0; i < 15; i++) current_prog.stad_delay = 500; |
На сегодня всё, до новых встреч!