- Минимальное количество занятых пинов микроконтроллера
- Дополнительное железо (сдвиговики, ШИМ контроллеры) использовать нельзя!
- Простой набор цветов (скажем 8 цветов)
- Индивидуальное управление каждым светодиодом
На схеме RGB светодиоды с общим катодом подключены следующим образом:
- Ноги 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 вынимаем нужную единичку/нолик и подаём её на цветной пин текущего светодиода. Вот и вся магия =) Видео с демонстрацией данного скетча/схемы можно посмотреть ниже.



