- Минимальное количество занятых пинов микроконтроллера
- Дополнительное железо (сдвиговики, ШИМ контроллеры) использовать нельзя!
- Простой набор цветов (скажем 8 цветов)
- Индивидуальное управление каждым светодиодом
- Ноги R, G, B соединены параллельно и через резисторы заведены на пины 10, 11, 12 (пины неважно какие, в скетче можно изменить). Примечание: индикация динамическая, т.е. светодиоды горят по очереди, и чем их больше – тем меньше по времени горит каждый. В принципе можно уменьшать сопротивление резистора хоть до 100 Ом, светодиоды не должны сгореть! Я по привычке поставил 220 Ом и всё работало.
- Общие ноги (катоды) подключены опять же на любые пины, в схеме это A0, A1, A2, A3, A4
#include "GyverTimers.h" #define LED_AMOUNT 5 // сколько RGB ледов подключено byte COM_pins[] = {A0, A1, A2, A3, A4}; // общие пины диодов byte RGB_pins[] = {10, 11, 12}; // пины цветов (R, G, B) // цвета enum colors { D_BLACK, D_BLUE, D_GREEN, D_CYAN, D_RED, D_MAGENTA, D_YELLOW, D_WHITE, }; volatile colors color[LED_AMOUNT]; void setup() { // настройка динамического переключения // катоды врубаем for (byte i = 0; i < LED_AMOUNT; i++) { pinMode(COM_pins[i], OUTPUT); writePin(COM_pins[i], 1); } // аноды вырубаем for (byte i = 0; i < 3; i++) { pinMode(RGB_pins[i], OUTPUT); writePin(RGB_pins[i], 0); } // таймер на 4 миллисекунды Timer1.setPeriod(4000); Timer1.enableISR(); // задаём цвета color[0] = D_RED; color[1] = D_GREEN; color[2] = D_BLUE; color[3] = D_WHITE; color[4] = D_MAGENTA; // задержка (смотрим цвета) delay(3000); } void loop() { // включаем выключаем зажигаем разными цветами color[0] = D_BLACK; delay(100); color[1] = D_BLACK; delay(100); color[2] = D_BLACK; delay(100); color[3] = D_BLACK; delay(100); color[4] = D_BLACK; delay(100); color[0] = D_RED; delay(100); color[1] = D_GREEN; delay(100); color[2] = D_BLUE; delay(100); color[3] = D_WHITE; delay(100); color[4] = D_MAGENTA; delay(100); } ISR(TIMER1_A) { static volatile int8_t counter = 0; // выключаем предыдущий катод writePin(COM_pins[ (counter == 0) ? (LED_AMOUNT - 1) : (counter - 1) ], 1); // выставляем цвет for (byte i = 0; i < 3; i++) writePin(RGB_pins[i], bitRead(color[counter], i)); // включаем следующий катод writePin(COM_pins[counter], 0); // зацикливаем счётчик if (++counter >= LED_AMOUNT) counter = 0; } void writePin(uint8_t pin, uint8_t x) { if (pin < 8) bitWrite(PORTD, pin, x); else if (pin < 14) bitWrite(PORTB, (pin - 8), x); else if (pin < 20) bitWrite(PORTC, (pin - 14), x); else return; }
Что делает код? Давайте разберём. Начнём с цветов: цвета у меня записаны как перечисления (enum):
// цвета enum colors { D_BLACK, D_BLUE, D_GREEN, D_CYAN, D_RED, D_MAGENTA, D_YELLOW, D_WHITE, };
Как вы знаете из урока про enum, каждый добавленный в список член получает “значение” на единицу больше предыдущего, т.е.
D_BLACK == 0 D_BLUE == 1 D_GREEN == 2 D_CYAN == 3 D_RED == 4 D_MAGENTA == 5 D_YELLOW == 6 D_WHITE == 7
И что? Действительно. Давайте запишем в бинарном виде:
D_BLACK == 0b000 D_BLUE == 0b001 D_GREEN == 0b010 D_CYAN == 0b011 D_RED == 0b100 D_MAGENTA == 0b101 D_YELLOW == 0b110 D_WHITE == 0b111
Вооот оно что, я думаю вы уже уловили, откуда берутся цвета и почему их всего 8. Цвета светодиодов хранятся в массиве colors и задаются вот так:
// задаём цвета color[0] = D_RED; color[1] = D_GREEN; color[2] = D_BLUE; color[3] = D_WHITE; color[4] = D_MAGENTA;
Это ладно, самое интересное происходит в прерывании таймера!
void isr() { static volatile int8_t counter = 0; // выключаем предыдущий катод writePin(COM_pins[ (counter == 0) ? (LED_AMOUNT - 1) : (counter - 1) ], 1); // выставляем цвет for (byte i = 0; i < 3; i++) writePin(RGB_pins[i], bitRead(color[counter], i)); // включаем следующий катод writePin(COM_pins[counter], 0); // зацикливаем счётчик if (++counter >= LED_AMOUNT) counter = 0; }
Что тут происходит? Функция writePin является очень быстрым аналогом digitalWrite и работает только для ATmega328 (UNO, Nano, Pro Mini), если вам нужно запустить код на другом чипе – замените writePin на digitalWrite. Но может подтормаживать.
Со включением и выключением катодов я думаю всё понятно, а вся хитрость с установкой цвета происходит тут:
for (byte i = 0; i < 3; i++) writePin(RGB_pins[i], bitRead(color[counter], i));
Мы берём тот самый цвет, который хранится в массиве color, это где мы смотрели бинарник, и при помощи функции-макроса bitRead вынимаем нужную единичку/нолик и подаём её на цветной пин текущего светодиода. Вот и вся магия =) Видео с демонстрацией данного скетча/схемы можно посмотреть ниже.