Светодиодная матрица MAX7219 и Arduino: бегущая строка с MD_Parola
MAX7219 и Arduino: подключение матриц 8x8 и модулей 4-в-1 8x32, бегущая строка с MD_Parola, исправление зеркального текста через FC16_HW, часы и код для ESP32.
MAX7219 — недорогая микросхема-драйвер, которая сама управляет 64 светодиодами, и это самый простой способ подключить светодиодную матрицу к Arduino. Обычно она встречается в двух вариантах: модуль с одной точечной матрицей 8x8 и модуль 4-в-1 с точечной матрицей 8x32, где четыре матрицы стоят рядом. MAX7219 нужны всего 3 сигнальных провода от Arduino (DIN, CS, CLK): микросхема сама сканирует строки сотни раз в секунду, задаёт яркость и помнит состояние каждого светодиода. Arduino остаётся свободной для бегущей строки, часов, табло или анимаций, а дисплей можно сделать сколь угодно длинным, соединяя модули друг за другом.
В этом руководстве разберём, как работает MAX7219, как подключить модуль к Arduino Uno, ESP32 и Raspberry Pi Pico по SPI, как соединять модули в цепочку и как вывести первую бегущую строку с библиотекой MD_Parola. Затем исправим проблему перевёрнутого или зеркального текста, на которой спотыкается большинство новичков, выбрав правильный тип оборудования (FC16_HW), рассчитаем яркость и потребление, выведем символы с диакритикой и собственные символы, а также соберём часы с RTC-модулем и попиксельную анимацию. В последних разделах — отправка сообщений с телефона через ESP32, код на MicroPython для Pico и решения самых частых ошибок.

Что такое MAX7219 и как он работает?
64 светодиода невозможно подключить к отдельным пинам Arduino, поэтому в светодиодных матрицах используется мультиплексирование. В матрице 8x8 светодиоды расположены в сетке из 8 строк и 8 столбцов. В каждый момент MAX7219 включает только одну группу светодиодов (в даташите это «digit», разряд; на матрице это строка или столбец), через остальные линии выбирает, какие из восьми светодиодов этой группы загорятся, и переходит к следующей группе. Все восемь групп сканируются примерно 800 раз в секунду; глаз не успевает за такой скоростью, поэтому изображение кажется неподвижным. При этом микросхема хранит рисунок каждой группы в собственной ОЗУ 8x8 бит, ограничивает ток светодиодов одним резистором и регулирует яркость в 16 ступеней.
Arduino общается с MAX7219 по 3-проводному последовательному интерфейсу, который очень похож на SPI. Каждая команда — 16-битный пакет: старший байт выбирает регистр для записи, младший несёт значение. Пока CS (в даташите LOAD) в состоянии LOW, биты вдвигаются с DIN по каждому нарастающему фронту CLK; когда CS снова становится HIGH, пакет записывается в регистр. Идея та же, что у сдвигового регистра 74HC595, но MAX7219 к тому же сам занимается сканированием и ограничением тока, так что работы гораздо меньше. Как устроен SPI в целом, рассказано в статье Что такое SPI?.
| Характеристика | Значение |
|---|---|
| Напряжение питания | 4,0–5,5 V (на практике 5 V) |
| Интерфейс | 3-проводный последовательный (DIN, CLK, CS), до 10 MHz |
| Управляемые светодиоды | 64 (матрица 8x8 или восемь разрядов 7-сегментного индикатора) |
| Частота сканирования | Обычно 800 Hz (8 разрядов) |
| Ток сегмента | Задаётся резистором RSET, максимум 40 mA (на модулях обычно 10 kΩ) |
| Яркость | 16 ступеней, коэффициент заполнения от 1/32 до 31/32 |
| Ток в режиме отключения | Около 150 µA |
| Порог входного уровня HIGH | Не менее 3,5 V (при питании 5 V) |
| Соединение в цепочку | Выход DOUT на вход DIN следующей микросхемы |
У MAX7219 14 адресуемых регистров, но для светодиодной матрицы нужны те, что в таблице ниже. Библиотеки настраивают их за тебя; и всё же понимание их назначения очень помогает при отладке. Например, при первой подаче питания микросхема стартует в режиме shutdown (отключение) с тёмным дисплеем, поэтому если код инициализации не выполнился, модуль выглядит совершенно мёртвым. Родственная микросхема MAX7221 имеет те же регистры, но у неё настоящий SPI-совместимый пин CS и драйверы сегментов с ограничением скорости нарастания фронтов, которые снижают электромагнитные помехи.
| Адрес | Регистр | Назначение |
|---|---|---|
| 0x00 | No-Op (нет операции) | Оставляет эту микросхему без изменений, пока пакет идёт к другой микросхеме цепочки |
| 0x01–0x08 | Digit 0–7 (разряды) | 8-битный рисунок светодиодов каждой из восьми строк (или столбцов) |
| 0x09 | Decode Mode (режим декодирования) | BCD-декодирование для 7-сегментных разрядов; для матрицы выключено (0x00) |
| 0x0A | Intensity (яркость) | Яркость, от 0x0 до 0xF |
| 0x0B | Scan Limit (число разрядов) | Сколько разрядов сканировать; для матрицы 7 (все восемь) |
| 0x0C | Shutdown (отключение) | 0: режим отключения, 1: нормальная работа |
| 0x0F | Display Test (тест дисплея) | 1: все светодиоды горят на полной яркости |
Одиночный модуль 8x8 или модуль 4-в-1 8x32?
Два самых распространённых модуля собраны по одной схеме; разница в том, как светодиодная матрица подключена к MAX7219, и в форме платы. Одиночный модуль на зелёной плате несёт MAX7219 в корпусе DIP (24 вывода) и матрицу 8x8, например 1088AS; с одного края у него входные пины (VCC, GND, DIN, CS, CLK), с другого — выходные (VCC, GND, DOUT, CS, CLK). На синем модуле 4-в-1 четыре микросхемы MAX7219 в корпусе SMD стоят под матрицами, а четыре блока уже соединены в цепочку на заводе; плата обычно маркирована FC-16. Для длинной бегущей строки модуль 4-в-1 и дешевле, и аккуратнее.

| Характеристика | Одиночный модуль 8x8 | Модуль 4-в-1 8x32 (FC-16) |
|---|---|---|
| Разрешение | 8x8 = 64 светодиода | 32x8 = 256 светодиодов |
| Количество MAX7219 | 1 (корпус DIP) | 4 (корпус SMD) |
| Тип оборудования в коде | Обычно GENERIC_HW | Обычно FC16_HW |
| Символов на экране одновременно | 1 | 4–6 (зависит от ширины букв) |
| Максимальный ток на полной яркости | Около 330 mA | Около 1,3 A |
| Лучше всего подходит для | Иконки, кубики, смайлики, небольшие игры | Бегущая строка, часы, табло |
Подключение MAX7219: Arduino Uno, ESP32 и Pico
У модуля пять пинов: VCC и GND для питания, DIN для данных, CLK для тактового сигнала и CS для выбора микросхемы (загрузки). По умолчанию библиотеки MD_Parola и MD_MAX72XX используют аппаратный SPI, поэтому DIN нужно подключить к пину MOSI твоей платы, а CLK — к пину SCK; CS может быть любым цифровым пином. Где на твоей плате находятся пины аппаратного SPI, можно посмотреть в утилите распиновки. Провода всегда подключай к входной стороне (IN) модуля, к краю с надписью DIN; край с надписью DOUT предназначен для следующего модуля.
| Пин модуля | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC | 5V | 5V (VIN) | VBUS (5 V) |
| GND | GND | GND | GND |
| DIN | D11 (MOSI) | GPIO 23 (MOSI) | GP19 (SPI0 TX) |
| CS | D10 | GPIO 5 | GP17 |
| CLK | D13 (SCK) | GPIO 18 (SCK) | GP18 (SPI0 SCK) |
У ESP32 и Pico логика на 3,3 V, а даташит MAX7219 при питании 5 V требует для уровня HIGH не менее 3,5 V. Получается, что сигналы 3,3 V формально чуть ниже границы. Большинство модулей всё равно работают, но с длинными проводами или с некоторыми партиями микросхем могут появляться случайные точки и искажённое изображение. Надёжное решение — буфер вроде 74HCT125, который принимает 3,3 V как HIGH и выдаёт 5 V, или быстрый преобразователь логических уровней на линиях DIN, CLK и CS. Часто проблему решает и укорачивание сигнальных проводов до 20 см и меньше.
Соединение модулей в цепочку (daisy chain)
Пин DOUT микросхемы MAX7219 выдаёт данные, поступившие на DIN, через 16,5 такта. Если соединить сторону OUT одного модуля со стороной IN следующего, DOUT идёт на DIN следующей микросхемы, а CLK и CS общие для всех микросхем. Так от Arduino по-прежнему отходят всего 3 сигнальных провода, а дисплей может состоять из 8, 16 или 32 блоков. При каждом обновлении библиотека прогоняет пакеты по всей цепочке, и когда CS поднимается, все микросхемы обновляются одновременно. На модулях 4-в-1 эти соединения уже есть на плате; чтобы собрать дисплей из 8 блоков (64x8) из двух модулей 4-в-1, достаточно соединить пины OUT первого модуля с пинами IN второго и задать в коде MAX_DEVICES равным 8.
В библиотеке номера блоков начинаются с модуля, ближайшего ко входу DIN, и растут влево; у MD_MAX72XX начало координат — верхний правый угол дисплея. Поэтому вход DIN должен оставаться справа, иначе текст не будет читаться правильно. С ростом цепочки настоящим ограничением становится питание: каждые 4–8 блоков заново подводи 5 V и GND толстыми проводами и ставь на входе питания каждого модуля электролитический конденсатор 10 µF и керамический 100 nF. Эту пару рекомендует и даташит — она сглаживает пульсации от импульсов тока светодиодов.
Установка библиотек: MD_MAX72XX и MD_Parola
Две самые популярные библиотеки для MAX7219 написаны одним автором (majicDesigns) и работают вместе. MD_MAX72XX делает низкоуровневую работу: инициализирует модули, пишет пиксели и столбцы, управляет шрифтом. MD_Parola строится поверх неё и добавляет бегущую строку, эффекты, выравнивание и зоны. Библиотека LedControl из старых примеров тоже работает, но прокрутку текста там приходится писать вручную. Если библиотеки ещё не устанавливались, сначала прочитай Как добавить библиотеку в Arduino.
- В Arduino IDE открой «Менеджер библиотек» (Library Manager): «Инструменты › Управлять библиотеками» (Tools › Manage Libraries), и найди MD_Parola.
- Установи библиотеку MD_Parola от majicDesigns. Когда появится вопрос о зависимостях, выбери Установить все (Install all), чтобы установилась и MD_MAX72XX.
- Подключи модуль по таблице выше, а для модуля 4-в-1 задай в коде HARDWARE_TYPE как FC16_HW и MAX_DEVICES как 4.
- Загрузи скетч. Если текст выглядит перевёрнутым, зеркальным или повёрнутым, не страшно: это исправляется одной строкой в разделе о типе оборудования ниже.
Первая программа: бегущая строка с MD_Parola
1// MAX7219 4'lü modülde kayan yazı (MD_Parola) / Scrolling text on a 4-in-1 MAX7219 module (MD_Parola)
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5
6#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // mavi PCB'li 4'lü modül / blue-PCB 4-in-1 module
7#define MAX_DEVICES 4 // zincirdeki 8x8 blok sayısı / number of 8x8 blocks in the chain
8#define CS_PIN 10 // DIN = D11 (MOSI), CLK = D13 (SCK)
9
10MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
11
12void setup() {
13 display.begin();
14 display.setIntensity(4); // parlaklık 0–15 / brightness 0–15
15 display.displayClear();
16 // metin, hizalama, hız (ms/kare), bekleme (ms), giriş efekti, çıkış efekti
17 // text, alignment, speed (ms/frame), pause (ms), entry effect, exit effect
18 display.displayText("Merhaba BlueGrays!", PA_CENTER, 50, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
19}
20
21void loop() {
22 if (display.displayAnimate()) { // efekt bittiğinde true döner / returns true when the effect ends
23 display.displayReset(); // aynı yazıyı baştan başlat / restart the same text
24 }
25}Самая важная строка — displayText(): она по порядку принимает текст, выравнивание (PA_LEFT, PA_CENTER, PA_RIGHT), время между кадрами в миллисекундах, паузу после того, как текст остановится, и эффекты входа и выхода. Чем меньше значение скорости, тем быстрее движется текст; 25–60 мс дают читаемую прокрутку. displayAnimate() за каждый вызов продвигает анимацию максимум на один кадр и сразу возвращает управление. Поэтому её нужно постоянно вызывать в loop(), и поэтому в остальном коде не стоит использовать delay(), иначе текст будет дёргаться. Как ждать, не блокируя программу, рассказано в статье millis() или delay().
| Функция | Что делает |
|---|---|
| begin() | Инициализирует модули, выводит их из режима отключения и очищает дисплей |
| displayText(текст, выравнивание, скорость, пауза, эффект_входа, эффект_выхода) | Задаёт текст и эффекты для показа и запускает анимацию |
| displayAnimate() | Рисует следующий кадр; возвращает true, когда эффект закончен |
| displayReset() | Запускает тот же текст и эффект заново с начала |
| print("...") | Показывает неподвижный текст без эффекта |
| setIntensity(0–15) | Задаёт яркость всех модулей |
| setInvert(true) | Меняет местами горящие и погасшие светодиоды (негатив) |
| displayShutdown(true) | Переводит дисплей в режим отключения; false снова его включает |
| begin(2) и setZone(0, 0, 1) | Делит дисплей на независимые зоны, у каждой свой текст и эффект |
Кроме PA_SCROLL_LEFT есть много эффектов входа и выхода: PA_PRINT (текст появляется сразу), PA_SCROLL_UP и PA_SCROLL_DOWN (вертикальная прокрутка), PA_WIPE, PA_OPENING и PA_CLOSING (раскрытие из центра и сворачивание к центру), PA_FADE (плавное изменение яркости), PA_DISSOLVE, PA_BLINDS, PA_RANDOM и PA_GROW_UP. Чтобы увидеть их все, загрузи скетч Parola_Animation_Catalog из «Файл › Примеры › MD_Parola» (File › Examples); не забудь поменять строки HARDWARE_TYPE и MAX_DEVICES под свой модуль.
Выбор типа оборудования (FC16_HW) и исправление зеркального текста
Производители подключают матрицу 8x8 к MAX7219 по-разному: на одних платах линии «digit» микросхемы идут к строкам, на других — к столбцам, а порядок строк или столбцов может быть обратным. Библиотека компенсирует это настройкой HARDWARE_TYPE. Если тип выбран неправильно, модуль исправен, но буквы в каждом блоке 8x8 выглядят зеркальными, перевёрнутыми или повёрнутыми на 90 градусов. Поскольку в большинстве старых примеров в интернете стоит PAROLA_HW или GENERIC_HW, это самая частая проблема с синими модулями 4-в-1, которые продаются сегодня. В таблице ниже показано, что менять при каждом симптоме; она составлена для модуля 4-в-1 FC-16.
| Что видно | Вероятная причина | Решение |
|---|---|---|
| Буквы в каждом блоке зеркально отражены слева направо | Выбран PAROLA_HW, порядок столбцов обратный | Используй FC16_HW |
| Буквы в каждом блоке повёрнуты на 90 градусов | Выбран GENERIC_HW, строки и столбцы поменяны местами | Используй FC16_HW |
| Буквы в каждом блоке перевёрнуты вверх ногами | Выбран ICSTATION_HW, порядок строк и столбцов обратный | Используй FC16_HW |
| Весь текст перевёрнут и бежит слева направо | Модуль стоит наоборот, вход DIN слева | Поверни модуль на 180 градусов, чтобы вход DIN оказался справа |
| Правильно показывают только некоторые блоки, остальные пустые или с мусором | MAX_DEVICES не совпадает с реальным числом блоков | Задай MAX_DEVICES равным числу блоков |
Яркость и потребление
Яркость зависит от двух вещей. Первая — резистор RSET на модуле: микросхема подаёт на светодиоды ток примерно в 100 раз больше тока, вытекающего из пина ISET. На модулях обычно стоит 10 kΩ, что даёт пиковый ток чуть больше 40 mA для красных светодиодов и около 40 mA для синих и белых. Вторая — регистр Intensity, который задаётся программно: значение n от 0 до 15 держит светодиоды включёнными (2n+1)/32 каждого интервала сканирования, так что 0 — минимальная (1/32), а 15 — максимальная (31/32) яркость. А поскольку каждая строка горит только одну восьмую времени, даже на полной яркости один светодиод в среднем потребляет около 5 mA. Прямое напряжение для светодиодов разных цветов можно посмотреть в утилите напряжений светодиодов.
Средний ток модуля можно грубо оценить так: I ≈ 8 mA + 5 mA × (2n+1)/32 × L. Здесь 8 mA — собственное потребление микросхемы, n — значение яркости, L — число горящих светодиодов на этом модуле (0–64); 5 mA — одна восьмая от пикового тока 40 mA. Например, при яркости 15 и всех 64 горящих светодиодах получается 8 + 5 × 0,97 × 64 ≈ 318 mA, что совпадает с 330 mA из даташита. Бегущая строка обычно зажигает лишь 25–35% светодиодов, поэтому реальное потребление гораздо ниже. В таблице ниже — типичные значения для модуля 4-в-1.
| Условие | На один модуль | Модуль 4-в-1 (32x8) |
|---|---|---|
| Все светодиоды горят, яркость 15 | ≈ 320 mA | ≈ 1,3 A |
| Все светодиоды горят, яркость 7 | ≈ 160 mA | ≈ 630 mA |
| Бегущая строка (~30% светодиодов), яркость 7 | ≈ 55 mA | ≈ 210 mA |
| Бегущая строка (~30% светодиодов), яркость 2 | ≈ 25 mA | ≈ 90 mA |
| Режим отключения (displayShutdown) | ≈ 0,15 mA | ≈ 0,6 mA |
Чтобы ночью дисплей не слепил, яркость можно регулировать автоматически с датчиком освещённости LDR, а когда рядом никого нет, усыплять дисплей вызовом displayShutdown(true). Для вывески с питанием от батареи введи значения из таблицы в калькулятор времени работы от батареи и узнай, на сколько часов её хватит. В помещении вместо 15 обычно достаточно яркости около 2–4: так мягче для глаз, а ток светодиодов падает примерно в три–шесть раз.
Символы с диакритикой и специальные символы (UTF-8)
Arduino IDE сохраняет код в UTF-8, поэтому буква вроде «é» или «ü» в тексте занимает не один байт, а два. MD_Parola ищет каждый байт в шрифте как номер символа, и буквы с диакритикой превращаются в бессмысленные значки. Стандартный шрифт библиотеки содержит весь набор Latin-1 (é, è, à, ñ, ç, ä, ö, ü, ß и другие), так что достаточно небольшой функции, которая переводит UTF-8 в коды шрифта. Буквы вне Latin-1, например турецкие ğ, ı и ş в примере ниже, приходится рисовать попиксельно и добавлять через addChar(). Скетч выполняет оба шага.
1// MAX7219 + MD_Parola: Türkçe karakterli kayan yazı / Scrolling text with Turkish letters
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5
6#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
7#define MAX_DEVICES 4
8#define CS_PIN 10
9
10MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
11
12// Fontta olmayan 6 harf: ilk bayt genişlik, sonrakiler sütunlar (bit 0 = en üst satır)
13// 6 letters missing from the font: first byte is the width, then the columns (bit 0 = top row)
14const uint8_t trG[] = {5, 56, 69, 70, 85, 116}; // Ğ
15const uint8_t trg[] = {4, 25, 166, 166, 125}; // ğ
16const uint8_t trI[] = {3, 68, 125, 68}; // İ
17const uint8_t tri[] = {1, 124}; // ı
18const uint8_t trS[] = {5, 70, 73, 201, 73, 49}; // Ş
19const uint8_t trs[] = {4, 88, 212, 84, 52}; // ş
20
21// UTF-8 metni fontun kodlarına çevirir / converts UTF-8 text to the font's codes
22void utf8ToFont(const char *src, char *dst, size_t size) {
23 size_t n = 0;
24 while (*src && n < size - 1) {
25 uint8_t c = *src++;
26 if (c < 0x80) { dst[n++] = c; continue; } // ASCII aynen kalır / ASCII stays as is
27 if ((c & 0xE0) == 0xC0 && *src) { // 2 baytlık harf / 2-byte letter
28 uint16_t u = ((c & 0x1F) << 6) | (*src++ & 0x3F);
29 switch (u) {
30 case 0x11E: dst[n++] = (char)0x80; break; // Ğ
31 case 0x11F: dst[n++] = (char)0x81; break; // ğ
32 case 0x130: dst[n++] = (char)0x82; break; // İ
33 case 0x131: dst[n++] = (char)0x83; break; // ı
34 case 0x15E: dst[n++] = (char)0x84; break; // Ş
35 case 0x15F: dst[n++] = (char)0x85; break; // ş
36 default: dst[n++] = (u < 0x100) ? (char)u : '?'; // ç ö ü Ç Ö Ü ° fontta var / already in the font
37 }
38 continue;
39 }
40 while ((*src & 0xC0) == 0x80) src++; // desteklenmeyen karakteri atla / skip an unsupported character
41 dst[n++] = '?';
42 }
43 dst[n] = '\0';
44}
45
46char message[96];
47
48void setup() {
49 display.begin();
50 display.setIntensity(3);
51 display.addChar(0x80, trG); // addChar begin()'den sonra / addChar after begin()
52 display.addChar(0x81, trg);
53 display.addChar(0x82, trI);
54 display.addChar(0x83, tri);
55 display.addChar(0x84, trS);
56 display.addChar(0x85, trs);
57 utf8ToFont("Günaydın İstanbul! Şehirde hava 22°C, çok güzel bir gün.", message, sizeof(message));
58 display.displayText(message, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
59}
60
61void loop() {
62 if (display.displayAnimate()) display.displayReset();
63}В массиве каждого своего символа первый байт — ширина буквы в столбцах, а следующие байты — столбцы слева направо; бит 0 каждого байта соответствует верхней строке. Например, ı без точки занимает один столбец, а значение 124 (0b01111100) зажигает строки со 2-й по 6-ю; от обычной i она отличается только отсутствием точки сверху. У заглавных Ğ и İ тело буквы сделано на две строки короче (5 строк вместо 7), чтобы осталось место для надстрочного знака. addChar() не копирует данные, а лишь запоминает их адрес, поэтому массивы должны быть глобальными и добавляться после begin(). Коды 0x80–0x85 выбраны потому, что в обычном тексте они не встречаются. Точно так же можно добавить любой символ, например знак градуса, стрелку или сердечко.
Пример часов: цифровые часы с RTC-модулем

Счётчик millis() у Arduino сбрасывается при пропадании питания, а из-за керамического резонатора на Uno может уходить на несколько минут в сутки. Для настоящих часов используй модуль часов реального времени (RTC). Подключи модуль DS1307 к шине I2C (SDA → A4, SCL → A5, VCC → 5V, GND → GND); MAX7219 использует пины SPI, так что модули друг другу не мешают. Для кода установи из «Менеджера библиотек» библиотеку RTClib от Adafruit.
1// MAX7219 + DS1307 RTC ile dijital saat / Digital clock with MAX7219 + DS1307 RTC
2#include <MD_Parola.h>
3#include <MD_MAX72xx.h>
4#include <SPI.h>
5#include <RTClib.h> // Adafruit RTClib
6
7#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
8#define MAX_DEVICES 4
9#define CS_PIN 10
10
11MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
12RTC_DS1307 rtc; // SDA = A4, SCL = A5
13
14char timeText[8]; // "12:34" + sonlandırıcı / + terminator
15uint8_t lastSecond = 99;
16
17void setup() {
18 display.begin();
19 display.setIntensity(2);
20 display.setTextAlignment(PA_CENTER); // print() bu hizalamayı kullanır / print() uses this alignment
21 if (!rtc.begin()) {
22 display.print("RTC?"); // modül bulunamadı / module not found
23 while (true) {}
24 }
25 if (!rtc.isrunning()) {
26 rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // ilk kurulumda derleme saati / compile time on first run
27 }
28}
29
30void loop() {
31 DateTime now = rtc.now();
32 if (now.second() != lastSecond) { // saniyede bir güncelle / update once a second
33 lastSecond = now.second();
34 // ':' ve boşluk aynı genişlikte, yazı kaymaz / ':' and space are equally wide, nothing shifts
35 sprintf(timeText, (lastSecond % 2) ? "%02d %02d" : "%02d:%02d", now.hour(), now.minute());
36 display.print(timeText);
37 }
38}Скетч раз в секунду читает время и мигает двоеточием. В стандартном шрифте двоеточие (:) и пробел одной ширины (2 столбца), поэтому цифры не прыгают. При первом запуске RTC выставляется на момент компиляции скетча. DS1307 может уходить примерно на минуту в месяц; для более точных часов возьми модуль DS3231, используй RTC_DS3231 вместо RTC_DS1307 и замени условие if (!rtc.isrunning()) на if (rtc.lostPower()). Благодаря кварцу с температурной компенсацией DS3231 уходит лишь примерно на минуту в год. Ещё дисплей можно разделить на две зоны и показывать на одной время, а на другой температуру.
Анимация: попиксельное рисование с MD_MAX72XX
Чтобы выйти за рамки бегущей строки, можно напрямую использовать библиотеку MD_MAX72XX. Она рассматривает всю цепочку как один пиксельный дисплей: у модуля 4-в-1 32 столбца и 8 строк. setPoint(row, column, state) включает или выключает один светодиод, setColumn() записывает сразу все 8 бит столбца, а clear() очищает дисплей. Начало координат (0, 0) — верхний правый угол; номера столбцов растут влево, номера строк — вниз. Код ниже рисует мячик, который отскакивает от краёв дисплея:
1// MD_MAX72XX ile seken top animasyonu / Bouncing ball animation with MD_MAX72XX
2#include <MD_MAX72xx.h>
3#include <SPI.h>
4
5#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
6#define MAX_DEVICES 4
7#define CS_PIN 10
8
9MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
10
11int x = 0, y = 0; // topun konumu (sütun, satır) / ball position (column, row)
12int dx = 1, dy = 1; // hareket yönü / direction of travel
13
14void setup() {
15 mx.begin();
16 mx.control(MD_MAX72XX::INTENSITY, 2);
17 mx.clear();
18}
19
20void loop() {
21 const int lastCol = mx.getColumnCount() - 1; // 4 modülde 31 / 31 with 4 modules
22 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF); // çizim bitene kadar gönderme / hold updates while drawing
23 mx.setPoint(y, x, false); // eski konumu sil / erase the old position
24 x += dx;
25 y += dy;
26 if (x <= 0 || x >= lastCol) dx = -dx; // kenara çarpınca dön / bounce off the edge
27 if (y <= 0 || y >= 7) dy = -dy;
28 mx.setPoint(y, x, true); // yeni konumu yak / light the new position
29 mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON); // hepsini tek seferde gönder / send everything at once
30 delay(40);
31}В каждом кадре сначала отключается автоматическое обновление (UPDATE), стирается старый пиксель и рисуется новый, затем UPDATE снова включается, и изменения уходят в модули за один раз. Так на дисплее не появляется недорисованный кадр. Тем же способом можно нарисовать кубик, смайлики, игру «Змейка» или индикатор уровня звука. В примерах MD_MAX72XX есть и готовые анимации: Pac-Man, глаза робота и песочные часы. При работе с MD_Parola к тем же функциям рисования можно добраться через getGraphicObject().
Сообщения через интернет с ESP32: бегущая строка с телефона
С ESP32 бегущую строку можно сделать беспроводной. Код ниже подключается к домашней сети, сначала прокручивает на дисплее свой IP-адрес, а затем показывает время, полученное из интернета (NTP). Открой этот IP-адрес в браузере телефона, подключённого к той же сети Wi-Fi, и появится текстовое поле; введённое сообщение начнёт бежать со следующего круга и будет чередоваться с часами. Модуль подключается к пинам VSPI, как в таблице: CLK к GPIO 18, DIN к GPIO 23 и CS к GPIO 5. Если ESP32 пока незнакома, сначала прочитай Что такое ESP32?.
1// ESP32: telefondan mesaj + internet saati / ESP32: message from your phone + internet time
2#include <WiFi.h>
3#include <WebServer.h>
4#include <time.h>
5#include <MD_Parola.h>
6#include <MD_MAX72xx.h>
7#include <SPI.h>
8
9#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
10#define MAX_DEVICES 4
11#define CS_PIN 5 // VSPI: CLK = GPIO 18, DIN = GPIO 23
12
13const char* ssid = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
14const char* password = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
15
16MD_Parola display = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
17WebServer server(80);
18
19char message[128];
20char nextMessage[128];
21char clockText[8] = "--:--";
22bool newMessage = false;
23bool showClock = false;
24
25const char PAGE[] = R"rawliteral(<!DOCTYPE html><html><head><meta charset="utf-8">
26<meta name="viewport" content="width=device-width"><title>MAX7219</title></head><body>
27<h2>Kayan yazı / Scrolling text</h2>
28<form action="/"><input name="m" maxlength="100"> <button>Gönder / Send</button></form>
29</body></html>)rawliteral";
30
31void handleRoot() {
32 if (server.hasArg("m")) { // formdan gelen mesaj / message from the form
33 server.arg("m").toCharArray(nextMessage, sizeof(nextMessage));
34 newMessage = true;
35 }
36 server.send(200, "text/html; charset=utf-8", PAGE);
37}
38
39void setup() {
40 display.begin();
41 display.setIntensity(3);
42 display.print("WiFi..");
43 WiFi.begin(ssid, password);
44 while (WiFi.status() != WL_CONNECTED) delay(500);
45 configTime(3 * 3600, 0, "pool.ntp.org"); // Türkiye UTC+3 / Turkey is UTC+3
46 snprintf(message, sizeof(message), "IP: %s", WiFi.localIP().toString().c_str());
47 server.on("/", handleRoot);
48 server.begin();
49 display.displayText(message, PA_LEFT, 40, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
50}
51
52void loop() {
53 server.handleClient();
54 if (display.displayAnimate()) { // önceki gösterim bitti / the previous show has ended
55 showClock = !showClock;
56 if (newMessage) { // yeni mesaj sırayı keser / a new message jumps the queue
57 strlcpy(message, nextMessage, sizeof(message));
58 newMessage = false;
59 showClock = false;
60 }
61 if (showClock) {
62 struct tm t;
63 if (getLocalTime(&t, 10)) strftime(clockText, sizeof(clockText), "%H:%M", &t);
64 display.displayText(clockText, PA_CENTER, 40, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
65 } else {
66 display.displayText(message, PA_LEFT, 40, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
67 }
68 }
69}handleRoot() не отправляет сообщение из формы сразу на дисплей, а откладывает его в nextMessage. Сообщение копируется только после окончания текущей анимации (когда displayAnimate() возвращает true), поэтому текст, который читает MD_Parola, никогда не меняется на середине. Значение 3 × 3600 в configTime() — часовой пояс UTC+3, принятый в Турции (как и в Москве); для Великобритании замени эту строку на configTzTime("GMT0BST,M3.5.0/1,M10.5.0", "pool.ntp.org"), которая учитывает и переход на летнее время. Для сообщений с диакритикой добавь в этот скетч строки addChar() и функцию utf8ToFont() из примера выше и замени строку strlcpy() на utf8ToFont(nextMessage, message, sizeof(message)). Про время из интернета и веб-панели в реальном времени читай в статьях Часы на ESP32 по NTP и Веб-сервер и WebSocket на ESP32.
MAX7219 с Raspberry Pi Pico и MicroPython
На Pico с MicroPython удобнее всего скопировать на плату через Thonny открытый драйвер max7219.py (micropython-max7219). Драйвер использует модуль MicroPython framebuf, поэтому функции рисования вроде text(), pixel(), line() и rect() доступны сразу, а на модулях 4-в-1 FC-16 изображение выводится в правильной ориентации. Питай модуль от VBUS (5 V) и подключи DIN к GP19, CLK к GP18 и CS к GP17. MicroPython занимает ещё и пин MISO шины SPI0 (GP16), так что не используй этот пин ни для чего другого. Шрифт framebuf размером 8x8 пикселей и содержит только ASCII, поэтому модуль 4-в-1 показывает по 4 буквы, а буквы с диакритикой не отображаются. Про настройку читай Что такое Raspberry Pi Pico? и Основы MicroPython.
1# Pico + 4'lü MAX7219 modül: kayan yazı / Pico + 4-in-1 MAX7219 module: scrolling text
2from machine import Pin, SPI
3import time
4import max7219 # max7219.py Pico'da olmalı / must be on the Pico
5
6# GP18 = SCK -> CLK, GP19 = MOSI -> DIN, GP17 -> CS; GP16 (MISO) boş kalır / GP16 (MISO) stays free
7spi = SPI(0, baudrate=5_000_000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19))
8display = max7219.Matrix8x8(spi, Pin(17, Pin.OUT), 4)
9display.brightness(3) # parlaklık 0–15 / brightness 0–15
10
11def scroll(text, delay_ms=35):
12 width = len(text) * 8 # her harf 8 piksel / each letter is 8 px wide
13 for x in range(32, -width, -1): # sağdan girip solda kaybolur / enters right, leaves left
14 display.fill(0)
15 display.text(text, x, 0, 1)
16 display.show()
17 time.sleep_ms(delay_ms)
18
19while True:
20 scroll("BlueGrays + Pico")
21 display.fill(0)
22 display.text("Pico", 0, 0, 1) # 4 harf tam sığar / exactly 4 letters fit
23 display.show()
24 time.sleep(1)
25 display.fill(0)
26 display.rect(0, 0, 32, 8, 1) # ekran çerçevesi / display frame
27 display.show()
28 time.sleep_ms(500)Идеи проектов на MAX7219
- Табло с бегущей строкой: собери информационное табло для магазина, класса или двери комнаты и меняй сообщение с телефона через модуль Bluetooth HC-05.
- Часы, календарь и температура: по очереди показывай время и дату с RTC, а также температуру и влажность с датчика DHT11.
- Интернет-часы и погода: прокручивай время NTP и температуру из погодного API с помощью ESP32.
- Табло для счёта: сделай дисплей с двумя зонами для матча по настольному теннису или баскетболу с управлением от ИК-пульта.
- Мини-игры: «Змейка», Pong или игра с уклонением от препятствий на дисплее 32x8 с джойстиком; больше идей в статье Игровые проекты на Arduino.
- Индикатор уровня звука: выводи показания датчика звука в виде столбчатой диаграммы из 32 столбцов.
- Анимационная панель 32x32: поставь четыре модуля 4-в-1 друг на друга и запускай анимации вроде дождя, звёзд и фейерверков.
Частые ошибки и как их исправить
| Симптом | Вероятная причина | Решение |
|---|---|---|
| Не горит ни один светодиод | Нет питания, провода подключены к стороне OUT модуля или пин CS не совпадает с кодом | Подключи провода к стороне IN с надписью DIN, проверь CS_PIN и питание 5 V |
| Все светодиоды горят в полную силу и не меняются | Данные не доходят: DIN и CLK перепутаны или микросхема застряла в тестовом режиме | Подключи DIN к MOSI, CLK к SCK, затем отключи и снова подай питание на модуль |
| Буквы зеркальные, перевёрнутые или повёрнуты на 90 градусов | Неправильный HARDWARE_TYPE | Попробуй FC16_HW на модуле 4-в-1 и GENERIC_HW на одиночном модуле |
| Весь текст перевёрнут и бежит слева направо | Модуль установлен наоборот, вход DIN слева | Поверни модуль на 180 градусов: вход DIN должен быть справа |
| Работает только один блок или текст выглядит обрезанным | Неправильный MAX_DEVICES или обрыв связи между модулями | Задай MAX_DEVICES равным числу блоков и проверь провода OUT → IN |
| Arduino перезагружается или изображение мерцает на большой яркости | Превышен лимит USB в 500 mA, и напряжение проседает | Используй внешний блок питания 5 V 2 A, объедини земли и снизь яркость |
| Случайные точки и искажённые символы на ESP32 | Сигнал 3,3 V ниже порога HIGH в 3,5 V, а провода длинные | Подними уровень с помощью 74HCT125 и укороти провода |
| Буквы с диакритикой выводятся бессмысленными значками | Байты UTF-8 идут прямо в шрифт | Используй подход с utf8ToFont() и addChar() |
| Бегущая строка запинается и дёргается | В loop() есть delay() или долгая задача | Вызывай displayAnimate() часто, а ожидания делай через millis() |
MAX7219 — один из самых дешёвых способов получить эффектный дисплей всего на нескольких проводах: микросхема берёт на себя сканирование, ограничение тока и яркость, так что можно сосредоточиться на том, что показывать. Начни с иконок на одиночном модуле 8x8, затем переходи к бегущей строке, часам и беспроводным сообщениям через ESP32 на модуле 4-в-1. Если нужны цветные светодиоды с индивидуальным управлением, хороший выбор — светодиоды WS2812B NeoPixel, а для проекта, который показывает только цифры, — 7-сегментный индикатор. В приложении BlueGrays есть проекты на MAX7219: точечные часы с календарём, метеостанция, анимации 32x8 и бегущая строка — пошагово, со схемами подключения и кодом.
Частые вопросы
Почему текст на модуле MAX7219 выглядит перевёрнутым или зеркальным?
Производители подключают матрицу 8x8 к MAX7219 по-разному, а библиотека компенсирует это настройкой HARDWARE_TYPE. Большинство синих модулей 4-в-1, которые продаются сегодня, правильно показывают с FC16_HW, а одиночные зелёные — с GENERIC_HW. Если изображение всё ещё неправильное, попробуй также PAROLA_HW и ICSTATION_HW: меняется всего одна строка кода.
Сколько модулей MAX7219 можно соединить в цепочку?
У самой микросхемы ограничения нет, а поскольку MD_MAX72XX хранит количество модулей в одном байте, программно допускается до 255. На практике предел задают питание и качество сигнала: каждые 4–8 блоков заново подводи к цепочке 5 V и GND, делай провода короткими и ставь на каждый модуль конденсаторы 10 µF и 100 nF. Чем длиннее цепочка, тем дольше длится каждое обновление и тем больше ОЗУ оно занимает.
Работает ли MAX7219 с ESP32 на 3,3 V?
MAX7219 нужно питание не меньше 4 V, поэтому питай модуль от пина 5V (VIN) платы ESP32. Даташит при питании 5 V требует для уровня HIGH 3,5 V; сигналы ESP32 на 3,3 V работают на большинстве модулей, но они ниже этой границы. Для надёжной работы поставь на линии DIN, CLK и CS преобразователь уровней, например 74HCT125.
Какой ток потребляет светодиодная матрица MAX7219?
Одиночный модуль 8x8 со всеми горящими светодиодами на полной яркости потребляет около 330 mA, а модуль 4-в-1 8x32 — до 1,3 A. В режиме бегущей строки горит только часть светодиодов, а яркость обычно держат от 2 до 7, поэтому модуль 4-в-1 обычно потребляет около 100–250 mA. В режиме отключения каждая микросхема потребляет всего около 150 µA.
Как вывести на MAX7219 буквы с диакритикой или специальные символы?
Стандартный шрифт MD_Parola содержит символы Latin-1, например é, ñ, ä, ö, ü и ß, но Arduino IDE хранит их как два байта UTF-8. Перед выводом переведи текст в коды шрифта небольшой функцией. Символы вне Latin-1 можно нарисовать вручную и добавить через addChar().
Может ли MAX7219 управлять 7-сегментным индикатором?
Да. Изначально MAX7219 и создавался для 7-сегментных индикаторов с общим катодом на 8 разрядов, и у него есть режим BCD, в котором он сам декодирует цифры. Готовые 8-разрядные модули индикаторов на MAX7219 легко использовать с функцией setDigit() библиотеки LedControl. Индикаторы с общим анодом напрямую к этой микросхеме подключить нельзя.
Что собрать после прочтения
Все проекты
Часы и календарь на матрице (MAX7219, DS1307)
Светящееся табло для стола или стены, которое показывает время и дату бегущей строкой. На светодиодном дисплее 8x32 крупные цифры часов, а две точки между ними мигают каждую секунду; в начале каждой м

Часы, календарь и метеостанция на матрице (MAX7219, DS1307)
Табло с бегущей строкой для стола или стены, которое по очереди показывает время, дату и погоду в комнате. На светодиодном дисплее 8x32 сначала видна температура в градусах Цельсия и Фаренгейта, затем

Анимация и текст на 4 матрицах 32x8
Сложи четыре длинных светодиодных матричных модуля друг на друга, и получится квадратный экран из более чем тысячи светящихся точек. Сначала буквы сыплются сверху, как дождь в кино, затем открывается

4 матрицы 32x8 — набор из 6 анимаций
Четыре светодиодные панели 32x8, сложенные друг на друга, образуют квадратный экран, на котором по очереди идут шесть анимаций: падающий цифровой дождь, растущая спираль, клетки, которые сами рождаютс

Пиксельная змейка на матрице 8x8
Собери классическую игру «Змейка» своими руками и сыграй в неё. На цветной светодиодной матрице 8x8 ты джойстиком управляешь зелёной змейкой и ешь красные яблоки; с каждым яблоком змейка растёт, немно

Точное время из интернета через NTP на ESP32
ESP32 подключается к Wi-Fi и получает точное время с точностью до миллисекунд от серверов NTP (Network Time Protocol). Благодаря функции configTzTime() автоматически применяется часовой пояс Турции (TRT, UTC+3), а в странах с летним временем переходы обрабатываются автоматически по строке POSIX TZ. Время выводится в монитор порта и на необязательный I2C LCD. configTzTime() запускает службу SNTP, которая периодически пересинхронизирует часы.

Цифровые часы на ESP32 и DS1307 RTC (I2C + LCD)
В этом проекте время и дата из модуля часов реального времени (RTC) DS1307 выводятся на I2C LCD-экран 16x2. DS1307 — RTC с резервной батарейкой, который хранит время даже при выключенной ESP32. Оба модуля делят одну шину I2C (SDA/SCL), и ты узнаёшь, как управлять несколькими устройствами всего по двум сигнальным проводам. Проект даёт практические знания о протоколе I2C, работе модуля RTC и динамическом выводе данных на LCD-экран.

Цифровые часы на 4-разрядном семисегментном индикаторе
Светящиеся цифровые часы из четырёх разрядов, почти как на кухонной духовке. Модуль часов DS1302 хранит время, а Arduino каждую секунду читает часы и минуты и показывает их на четырёхразрядном индикат
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

