MAX7219: подключение к ESP32 и Arduino

Драйвер светодиодной матрицы 8×8

Матрица 8×8 светодиодов (или восемь цифр семисегментника) на трёх проводах. Модули соединяются цепочкой — из четырёх получается бегущая строка.

Как работает

MAX7219 сам сканирует 64 светодиода и держит яркость — контроллеру остаётся передать, какие точки горят. Интерфейс похож на SPI: данные на DIN, такт на CLK, защёлка на CS (LOAD). Выход DOUT одного модуля идёт на DIN следующего, так собирают табло из 4–8 матриц.

Библиотеки: LedControl — просто и наглядно (setLed, setRow, setDigit), MD_MAX72XX / MD_Parola — бегущий текст и эффекты.

Выводы

ВыводНазначение
CLKтактовый сигнал
CSзащёлка (LOAD): по фронту данные попадают в регистры
DINвход данных
VCCпитание 5 В
GNDобщий провод

Подключение к ESP32

MAX7219ESP32
CLKGPIO18
CSGPIO5
DINGPIO23
VCC3V3
GNDGND

Схема из примера «Матрица 8×8: первая точка».

⚡ В примере «MAX7219» питается от 3V3 — в симуляторе так проще, и модель это допускает. Настоящий модуль рассчитан на 5 В: подключайте VCC к VIN (5 В от USB) или к отдельному источнику 5 В с общей землёй.

Матрица 8×8: первая точка

Открыть пример целиком
Код скетча
// Выводы к микросхеме: данные, такт, выбор.
const int DIN = 23, CLK = 18, CS = 5;

// Регистры MAX7219. Разряды 1…8 — строки матрицы, остальное настройка.
const byte DECODE = 0x09, INTENSITY = 0x0A;
const byte SCANLIMIT = 0x0B, SHUTDOWN = 0x0C;

// Что горит: по байту на строку. Микросхема свои разряды прочитать не
// даёт, поэтому картинку скетч помнит сам.
byte rows[8];

// Слово в микросхему: адрес регистра и значение, старшим разрядом
// вперёд. Защёлкивается по возврату CS вверх — пока CS внизу, разряды
// просто едут дальше по цепочке.
void send(byte reg, byte value) {
  digitalWrite(CS, LOW);
  shiftOut(DIN, CLK, MSBFIRST, reg);
  shiftOut(DIN, CLK, MSBFIRST, value);
  digitalWrite(CS, HIGH);
}

void setup() {
  pinMode(DIN, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);

  send(SCANLIMIT, 7);       // работают все восемь строк
  send(DECODE, 0x00);       // знакогенератор не нужен: здесь точки, а не цифры
  send(INTENSITY, 8);
  // После подачи питания микросхема выключена: скетч без этой строки
  // работает правильно и не светит ничем.
  send(SHUTDOWN, 1);
  for (int row = 0; row < 8; row++) send(row + 1, 0);
}

void setLed(int row, int col, bool on) {
  // Старший разряд байта — левый столбец: так разведены модули.
  if (on) rows[row] |= 0x80 >> col;
  else rows[row] &= ~(0x80 >> col);
  send(row + 1, rows[row]);
}

void loop() {
  for (int row = 0; row < 8; row++) {
    for (int col = 0; col < 8; col++) {
      setLed(row, col, true);
      delay(40);
    }
  }
  // Строкой целиком — восемь точек за одну посылку вместо восьми.
  for (int row = 0; row < 8; row++) {
    rows[row] = 0;
    send(row + 1, 0);
    delay(60);
  }
}

Подключение к Arduino Uno

MAX7219Arduino Uno
CLKD13
CSD10
DIND11
VCC5V
GNDGND

Схема из примера «Arduino Uno: «Жизнь» Конвея на матрице 8×8».

Arduino Uno: «Жизнь» Конвея на матрице 8×8

Открыть пример целиком
Код скетча
// «Жизнь» Конвея на матрице 8×8.
//
// Клетка выживает, если у неё два или три живых соседа, и рождается на
// пустом месте, если соседей ровно три. Больше правил нет — а из них
// получаются мигалки, планеры и целые машины. Здесь поле замкнуто в
// тор: улетевший вправо планер прилетает слева. Если поле вымерло или
// застыло, засеваем новое.

#include <SPI.h>

const int CS = 10;
byte field[8], next[8];
unsigned long generation = 0;

void maxWrite(byte reg, byte v) {
  digitalWrite(CS, LOW); SPI.transfer(reg); SPI.transfer(v); digitalWrite(CS, HIGH);
}

bool alive(int x, int y) { return field[(y + 8) % 8] & (0x80 >> ((x + 8) % 8)); }

void seed() {
  // Планер и мигалка: чтобы сразу было на что посмотреть.
  memset(field, 0, 8);
  field[0] = 0b01000000;
  field[1] = 0b00100000;
  field[2] = 0b11100000;
  field[5] = 0b00001110;
  generation = 0;
  Serial.println(F("новое поле: планер и мигалка"));
}

void step() {
  for (int y = 0; y < 8; y++) {
    next[y] = 0;
    for (int x = 0; x < 8; x++) {
      int n = 0;
      for (int dy = -1; dy <= 1; dy++)
        for (int dx = -1; dx <= 1; dx++)
          if ((dx || dy) && alive(x + dx, y + dy)) n++;
      if (n == 3 || (n == 2 && alive(x, y))) next[y] |= 0x80 >> x;
    }
  }
  bool same = !memcmp(field, next, 8);
  memcpy(field, next, 8);
  generation++;
  int count = 0;
  for (int y = 0; y < 8; y++) for (byte b = field[y]; b; b &= b - 1) count++;
  Serial.print(F("поколение "));
  Serial.print(generation);
  Serial.print(F(", живых "));
  Serial.println(count);
  if (!count || same || generation > 200) seed();
}

void setup() {
  Serial.begin(9600);
  pinMode(CS, OUTPUT);
  SPI.begin();
  maxWrite(0x0F, 0); maxWrite(0x0C, 1); maxWrite(0x0B, 7); maxWrite(0x09, 0); maxWrite(0x0A, 4);
  seed();
}

void loop() {
  for (int y = 0; y < 8; y++) maxWrite(y + 1, field[y]);
  delay(300);
  step();
}

В железе

По паспорту MAX7219 при питании 5 В считает «единицей» от 3,5 В, а ESP32 выдаёт 3,3 В. Чаще всего работает, но надёжно — через преобразователь уровней (или питать матрицу от 4,0–4,5 В).

Частые ошибки

Другие детали