Введение
Светодиодная матрица 1088BS (8×8 точек) в чистом виде требует 16 пинов для прямого управления (8 строк + 8 столбцов) — что нереалистично при ограниченном количестве пинов Arduino. Входящий в комплект сдвиговый регистр 74HC595 позволяет управлять восемью независимыми выходами через всего три сигнальных пина, что и используется для управления одной координатой матрицы (обычно столбцами), тогда как строки управляются напрямую или вторым регистром. В статье разберём базовую схему вывода простых изображений на матрицу.
Концепция
74HC595 — последовательно-параллельный регистр сдвига: данные передаются по одному биту через пин DATA, синхронизируясь тактовым сигналом CLOCK, и после передачи всех 8 бит защёлкиваются на выходные пины импульсом на LATCH — Arduino предоставляет встроенную функцию shiftOut(), инкапсулирующую эту последовательность. Для матрицы 8×8 типична схема с быстрым последовательным перебором строк (только одна строка активна в любой момент очень короткое время) — за счёт инерции зрения человека быстрое переключение воспринимается как одновременное горение нужных точек (мультиплексирование), а не мерцание.
Пример кода
// firmware/src/main.cpp — вывод простого изображения на матрицу 8х8 через 74HC595
#define DATA_PIN 11
#define CLOCK_PIN 12
#define LATCH_PIN 8
#define ROW_PINS_START 2 // строки подключены напрямую к пинам 2-9
// Битовая карта простого "смайлика" — каждый байт описывает одну строку матрицы
byte smileyPattern[8] = {
0b00111100,
0b01000010,
0b10100101,
0b10000001,
0b10100101,
0b10011001,
0b01000010,
0b00111100
};
void setup()
{
pinMode(DATA_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
for (int i = 0; i < 8; i++) {
pinMode(ROW_PINS_START + i, OUTPUT);
}
Serial.begin(9600);
delay(100);
Serial.println("READY");
}
void loop()
{
// Мультиплексирование — отображаем картину построчно, очень быстро переключая строки
for (int row = 0; row < 8; row++) {
// Сначала гасим все строки, чтобы избежать "теней" от предыдущей активной строки
for (int i = 0; i < 8; i++) digitalWrite(ROW_PINS_START + i, LOW);
// Выводим через сдвиговый регистр битовую маску столбцов для текущей строки
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, smileyPattern[row]);
digitalWrite(LATCH_PIN, HIGH);
digitalWrite(ROW_PINS_START + row, HIGH); // активируем именно эту строку
delayMicroseconds(500); // короткая задержка — каждая строка "горит" доли миллисекунды
}
}
Пояснения к коду
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, smileyPattern[row]) передаёт один байт через сдвиговый регистр, выставляя на восьми его выходах нужную комбинацию включённых/выключенных столбцов для текущей строки — MSBFIRST определяет порядок передачи битов (старший бит первым), что должно соответствовать физическому порядку подключения выходов регистра к столбцам матрицы (при несовпадении изображение будет горизонтально отражено, что легко поправить либо физическим перемонтированием, либо инвертированием порядка бит программно).
Основной цикл loop() реализует мультиплексирование — вместо одновременного включения всех нужных светодиодов сразу (что для матрицы такого типа просто невозможно из-за общих анодов/катодов по строкам и столбцам), код быстро перебирает все 8 строк, для каждой устанавливая правильную комбинацию столбцов и активируя только эту одну строку на очень короткое время (500 микросекунд), после чего гасит её и переходит к следующей — при достаточно быстром переборе (полный цикл по всем 8 строкам происходит много раз в секунду) человеческий глаз воспринимает это как стабильно горящее изображение, а не мерцание.
Подводные камни
- Слишком долгая задержка между переключением строк (
delayMicrosecondsсо значением, заметно большим нескольких сотен микросекунд) делает мультиплексирование видимым как мерцание, особенно заметное на периферийном зрении или при движении глаз — для стабильного визуального восприятия полный цикл обновления всех 8 строк должен укладываться в доли миллисекунды, что означает очень короткую задержку на каждую отдельную строку. - Гашение всех строк перед выводом новой комбинации столбцов (явный цикл
forсLOWв начале каждой итерации внешнего цикла) обязательно для предотвращения «теней» — если не гасить предыдущую активную строку перед изменением данных в сдвиговом регистре, на короткое время может оказаться одновременно активна предыдущая строка с уже изменённой (для новой строки) комбинацией столбцов, что создаёт паразитные, нежелательные вспышки соседних светодиодов. - Перепутанный порядок битов (
MSBFIRSTпротивLSBFIRST) или неверное физическое подключение выходов регистра к столбцам матрицы проявляется как зеркально отражённое или иначе искажённое изображение — диагностика этой проблемы упрощается выводом простого, легко распознаваемого тестового паттерна (например, одна горящая точка в известном углу) перед попыткой вывести сложное изображение. - Большое количество одновременных операций
shiftOut()в условиях, когда прошивка также должна обрабатывать входящие команды черезSerial, может создать конфликт по времени — постоянное мультиплексирование матрицы требует почти непрерывного внимания процессора, и длинная блокирующая операция (например, ожидание полной команды черезSerial.available()без правильной неблокирующей структуры) может вызвать заметное мерцание матрицы в момент обработки команды; для проектов, объединяющих матрицу с другими задачами, особенно важна неблокирующая архитектураloop(), разобранная в статье 282.