Arduino

Светодиодная матрица 8х8 (1088BS) и сдвиговый регистр 74HC595

1 просмотров

Введение

Светодиодная матрица 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.