MAX7219 – микросхема для управления 7-сегментными индикаторами и матрицами 8х8. Благодаря встроенной динамической индикации и настройке тока этот чип в десятки раз упрощает работу и с теми, и с другими. На них можно выводить любую графику (геометрия, текст, изображения), модули легко масштабируются и на них можно сделать дисплей почти любого размера. На китайском рынке есть несколько вариантов исполнения матричных модулей на базе этой микросхемы.
![]() |
В наборе GyverKIT | START | IOT | EXTRA |
---|---|---|---|---|
Матрица | ✔ | ✔ |
В наборе GyverKIT старых партий были модули как слева, сейчас - как в центре. Подключаются и управляются они абсолютно одинаково, но первый вариант можно разобрать и отдельно поэкспериментировать с матрицей и микросхемой.
Подключение к Arduino #
Модуль подключается к питанию (VCC, GND), остальные пины – управляющие, могут подключаться:
- Все три (CLK, DI, CS) к любым цифровым пинам микроконтроллера
- CLK и DI – к пинам шины SPI, а CS – на любой цифровой пин. Как в этом уроке
Рассмотрим подключение по SPI к Arduino Nano и Wemos mini. CS на пин 5 (D1 у Wemos)
Модули обоих типов можно объединять в дисплеи, подключая вход (сторона с пином DI) каждой следующей матрицы к выходу (сторона с пином DO) предыдущей, с синими модулями это сделать проще:
Библиотека GyverMAX7219 позволяет сделать дисплей любого размера, подключая модули зигзагом:
Библиотеки #
- Max72xxPanel + Adafruit-GFX, нужно установить обе библиотеки
- GyverMAX7219 + GyverGFX, нужно установить обе библиотеки. При установке GyverMAX7219 через менеджер библиотек GyverGFX подтянется автоматически
GyverMAX7219 #
В примерах на этом сайте мы будем использовать GyverMAX7219. Объявление матрицы выглядит так:
MAX7219 < W, H, CS > mtrx; // подключение к аппаратному SPI
// пример: UNO / Nano (CLK - D13, DI - D11, CS - любой пин)
MAX7219 < W, H, CS, DATA, CLK > mtrx; // подключение к любым пинам
// W и H - количество МАТРИЦ по горизонтали и вертикали
// CS, DATA, CLK - номера пинов
Для начала работы нужно вызвать begin()
. Опционально можно настроить яркость через setBright(0.. 15)
и повернуть матрицу setRotation(0.. 3)
. На матрицу можно выводить:
void dot(int x, int y, uint8_t fill = 1); // точка, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void fastLineH(int y, int x0, int x1, uint8_t fill = 1); // вертикальная линия, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void fastLineV(int x, int y0, int y1, uint8_t fill = 1); // горизонтальная линия, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void line(int x0, int y0, int x1, int y1, uint8_t fill = 1); // линия, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void rect(int x0, int y0, int x1, int y1, uint8_t fill = 1); // прямоугольник, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void roundRect(int x0, int y0, int x1, int y1, uint8_t fill = 1); // скруглённый прямоугольник, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void circle(int x, int y, int radius, uint8_t fill = 1); // окружность, fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void bezier(uint8_t* arr, uint8_t size, uint8_t dense, uint8_t fill = 1); // кривая Безье
void bezier16(int* arr, uint8_t size, uint8_t dense, uint8_t fill = 1); // кривая Безье 16 бит. fill - GFX_CLEAR/GFX_FILL/GFX_STROKE
void drawBitmap(int x, int y, const uint8_t *frame, int width, int height, uint8_t invert = 0, byte mode = 0); // битмап
void setCursor(int x, int y); // установить курсор
void setScale(uint8_t scale); // масштаб текста
void invertText(bool inv); // инвертировать текст
void autoPrintln(bool mode); // автоматический перенос строки
void textDisplayMode(bool mode); // режим вывода текста GFX_ADD/GFX_REPLACE
Чтобы матрица обновилась – нужно вызвать update()
.
Примеры #
Инициализируем библиотеку для работы с одной матрицей, подключенной по SPI, пин CS на D5 (D1 на Wemos)
#include <GyverMAX7219.h>
MAX7219 < 1, 1, 5 > mtrx; // одна матрица (1х1), пин CS на D5
Инициализация дисплея с размером 4х2 матрицы (32×16 точек) будет выглядеть так:
MAX7219 < 4, 2, 5 > mtrx;
Пример с 1 матрицей
#include <GyverMAX7219.h>
MAX7219 < 1, 1, 5 > mtrx; // одна матрица (1х1), пин CS на D5
void setup() {
mtrx.begin(); // запускаем
mtrx.setBright(5); // яркость 0..15
//mtrx.setRotation(1); // можно повернуть 0..3, по 90 град по часовой стрелке
mtrx.dot(0, 0); // пиксель на координатах 0,0
mtrx.update(); // показать
delay(1000);
mtrx.clear();
// линии крест накрест
mtrx.line(0, 0, 7, 7); // (x0, y0, x1, y1)
mtrx.line(7, 0, 0, 7);
mtrx.update();
delay(1000);
mtrx.clear();
// круг
mtrx.circle(3, 3, 3, GFX_FILL); // х, у, радиус, заливка
mtrx.update();
delay(1000);
mtrx.clear();
// окружность
mtrx.circle(3, 3, 3, GFX_STROKE);
mtrx.update();
delay(1000);
mtrx.clear();
// остальную геометрию смотри в документации
}
void loop() {
}
Выводим кучу emoji картинок
#include <GyverMAX7219.h>
MAX7219 < 1, 1, 5 > mtrx; // одна матрица (1х1), пин CS на D5
// https://github.com/jorydotcom/matrix-emoji/blob/master/current-j5-emoji.js
// каждый массив - одby emoji
const uint8_t angryface_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0x18, 0x24, 0x42, 0x81};
const uint8_t circle_B[] PROGMEM = {0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c};
const uint8_t cdot_B[] PROGMEM = {0x3c, 0x42, 0x81, 0x99, 0x99, 0x81, 0x42, 0x3c};
const uint8_t donut_B[] PROGMEM = {0x3c, 0x7e, 0xff, 0xe7, 0xe7, 0xff, 0x7e, 0x3c};
const uint8_t equality_B[] PROGMEM = {0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00};
const uint8_t ball_B[] PROGMEM = {0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c};
const uint8_t thinsquare_B[] PROGMEM = {0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff};
const uint8_t thicksquare_B[] PROGMEM = {0xff, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xff};
const uint8_t centeredsquare1_B[] PROGMEM = {0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00};
const uint8_t centeredsquare2_B[] PROGMEM = {0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00};
const uint8_t arrowright_B[] PROGMEM = {0x00, 0x04, 0x06, 0xff, 0xff, 0x06, 0x04, 0x00};
const uint8_t arrowleft_B[] PROGMEM = {0x00, 0x20, 0x60, 0xff, 0xff, 0x60, 0x20, 0x00};
const uint8_t note_B[] PROGMEM = {0x04, 0x06, 0x07, 0x04, 0x3c, 0x7c, 0x7c, 0x38};
const uint8_t clock_B[] PROGMEM = {0x3c, 0x52, 0x91, 0x91, 0x8f, 0x81, 0x42, 0x3c};
const uint8_t heartoutline_B[] PROGMEM = {0x66, 0x99, 0x81, 0x81, 0x42, 0x24, 0x18, 0x00};
const uint8_t heartfull_B[] PROGMEM = {0x66, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00};
const uint8_t thincheck_B[] PROGMEM = {0x00, 0x00, 0x01, 0x02, 0x04, 0x88, 0x50, 0x20};
const uint8_t thickcheck_B[] PROGMEM = {0x00, 0x01, 0x03, 0x06, 0x8c, 0xd8, 0x70, 0x20};
const uint8_t speaker_B[] PROGMEM = {0x03, 0x07, 0x3f, 0x3f, 0x3f, 0x3f, 0x07, 0x03};
const uint8_t sound_B[] PROGMEM = {0x00, 0x40, 0x80, 0x00, 0xc0, 0x00, 0x80, 0x40};
const uint8_t xbig_B[] PROGMEM = {0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3};
const uint8_t target_B[] PROGMEM = {0x3c, 0x7e, 0xc3, 0xdb, 0xdb, 0xc3, 0x7e, 0x3c};
const uint8_t bell_B[] PROGMEM = {0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x00, 0x18};
const uint8_t smile_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0x00, 0x81, 0x42, 0x3c};
const uint8_t frown_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x42, 0x81};
const uint8_t winkright_B[] PROGMEM = {0x00, 0x60, 0x66, 0x00, 0x00, 0x81, 0x42, 0x3c};
const uint8_t winkleft_B[] PROGMEM = {0x00, 0x06, 0x66, 0x00, 0x00, 0x81, 0x42, 0x3c};
const uint8_t blink_B[] PROGMEM = {0x00, 0x00, 0x66, 0x00, 0x00, 0x81, 0x42, 0x3c};
const uint8_t laughing_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0xff, 0x81, 0x42, 0x3c};
const uint8_t tongueout_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0x00, 0x7e, 0x0a, 0x04};
const uint8_t expressionless_B[] PROGMEM = {0x00, 0x66, 0x66, 0x00, 0x00, 0xff, 0x00, 0x00};
// список массивов для вывода в цикле
const uint8_t* const emojis[] PROGMEM = {
angryface_B, circle_B, cdot_B, donut_B, equality_B, ball_B, thinsquare_B, thicksquare_B,
centeredsquare1_B, centeredsquare2_B, arrowright_B, arrowleft_B, note_B, clock_B, heartoutline_B, heartfull_B,
thincheck_B, thickcheck_B, speaker_B, sound_B, xbig_B, target_B, bell_B, smile_B,
frown_B, winkright_B, winkleft_B, blink_B, laughing_B, tongueout_B, expressionless_B,
};
void setup() {
mtrx.begin(); // запускаем
mtrx.setBright(5); // яркость 0..15
//mtrx.setRotation(1); // можно повернуть 0..3, по 90 град по часовой стрелке
// рисуем одну любую emoji
mtrx.drawBitmap(0, 0, winkleft_B, 8, 8);
mtrx.update();
delay(2000);
// выводим все для демонстрации
for (int i = 0; i < 31; i++) {
mtrx.clear();
uint16_t ptr = pgm_read_word(&(emojis[i]));// получаем адрес из таблицы ссылок
mtrx.drawBitmap(0, 0, ptr, 8, 8);
mtrx.update();
delay(1000);
}
}
void loop() {
}
Демо эффекты, дисплей 32х16 точек
// подключаем дисплей 32х16 (две сборки по 4 матрицы)
// демо 6 эффектов, меняются каждые 5 секунд
//#define MAX_SPI_SPEED 500000 // дефайн для изменения скорости SPI, по умолч 1000000
#include <GyverMAX7219.h>
#define AM_W 32 // 4 матрицы (32 точки)
#define AM_H 16 // 2 матрицы (16 точек)
// дисплей 4х2, пин CS 5, остальные на аппаратный SPI
MAX7219 < 4, 2, 5 > mtrx;
void setup() {
mtrx.begin();
mtrx.println("Mtrx");
mtrx.print("Demo");
mtrx.update();
delay(3000);
}
void loop() {
static uint32_t tmrM;
static byte mode;
if (millis() - tmrM >= 5000) { // 5 секунд
tmrM = millis();
if (++mode >= 6) mode = 0; // меняем режим от 0 до 5
mtrx.clear();
}
switch (mode) {
case 0: lines(); break;
case 1: ball(); break;
case 2: bigBall(); break;
case 3: net(); break;
case 4: bezier(); break;
case 5: bitmap(); break;
}
}
// редактор тут http://jorydotcom.github.io/matrix-emoji/
const uint8_t bmp[] PROGMEM = {
0b00100100,
0b00100100,
0b01111110,
0b11011011,
0b11111111,
0b11111111,
0b10100101,
0b00100100,
};
void bitmap() {
mtrx.clear();
static int x, y;
static int velX = 6, velY = 4;
x += velX;
y += velY;
if (x >= (AM_W - 8) * 10 || x < 0) velX = -velX;
if (y >= (AM_H - 8) * 10 || y < 0) velY = -velY;
mtrx.drawBitmap(x / 10, y / 10, bmp, 8, 8);
mtrx.update();
delay(30);
}
void net() {
const byte radius = 2;
const byte amount = 5;
static bool start = false;
static int x[amount], y[amount];
static int velX[amount], velY[amount];
if (!start) {
start = 1;
for (byte i = 0; i < amount; i++) {
x[i] = random(10, (AM_W - 1) * 10);
y[i] = random(10, (AM_H - 1) * 10);
velX[i] = random(2, 9);
velY[i] = random(2, 9);
}
}
mtrx.clear();
for (byte i = 0; i < amount; i++) {
x[i] += velX[i];
y[i] += velY[i];
if (x[i] >= (AM_W - 1 - radius) * 10 || x[i] < radius * 10) velX[i] = -velX[i];
if (y[i] >= (AM_H - 1 - radius) * 10 || y[i] < radius * 10) velY[i] = -velY[i];
mtrx.circle(x[i] / 10, y[i] / 10, radius);
}
for (int i = 0; i < amount; i++) {
for (int j = 0; j < amount; j++) {
if (i != j && dist(x[i] / 10, y[i] / 10, x[j] / 10, y[j] / 10) < 35) mtrx.line(x[i] / 10, y[i] / 10, x[j] / 10, y[j] / 10);
}
}
mtrx.update();
delay(20);
}
int dist(int x1, int y1, int x2, int y2) {
int lx = (x2 - x1);
int ly = (y2 - y1);
return (sqrt(lx * lx + ly * ly));
}
void bigBall() {
mtrx.clear();
byte radius = 3;
static int x = (AM_W / 2) * 10, y = (AM_H / 2) * 10;
static int velX = 9, velY = 5;
static bool fillFlag = 0;
x += velX;
y += velY;
if (x >= (AM_W - 4) * 10 || x < radius * 10) {
velX = -velX;
fillFlag = !fillFlag;
}
if (y >= (AM_H - 4) * 10 || y < radius * 10) {
velY = -velY;
fillFlag = !fillFlag;
}
mtrx.circle(x / 10, y / 10, radius, fillFlag ? GFX_STROKE : GFX_FILL);
mtrx.update();
delay(30);
}
void bezier() {
byte data[] = {0, 0, AM_W / 2, AM_H / 2, 0, AM_H - 1};
for (int i = 0; i < AM_W; i++) {
mtrx.clear();
data[0] = data[4] = AM_W - i;
data[2] = i;
mtrx.bezier(data, 3, 6);
mtrx.update();
delay(30);
}
for (int i = AM_W; i > 0; i--) {
mtrx.clear();
data[0] = data[4] = AM_W - i;
data[2] = i;
mtrx.bezier(data, 3, 6);
mtrx.update();
delay(30);
}
}
void lines() {
mtrx.clear();
for (byte i = 0; i < AM_W - 1; i += 3) {
mtrx.line(0, 0, i, AM_H);
mtrx.update();
delay(30);
}
for (int i = AM_H - 1; i >= 0 ; i -= 3) {
mtrx.line(0, 0, AM_W, i);
mtrx.update();
delay(30);
}
delay(100);
mtrx.clear();
for (int i = AM_W - 1; i > 0; i -= 3) {
mtrx.line(AM_W - 1, 0, i, AM_H);
mtrx.update();
delay(30);
}
for (int i = AM_H; i >= 0; i -= 3) {
mtrx.line(AM_W - 1, 0, 0, i);
mtrx.update();
delay(30);
}
delay(100);
}
void ball() {
static int x, y;
static int velX = 17, velY = 12;
x += velX;
y += velY;
if (x >= (AM_W - 1) * 10 || x < 0) velX = -velX;
if (y >= (AM_H - 1) * 10 || y < 0) velY = -velY;
mtrx.dot(x / 10, y / 10);
mtrx.update();
delay(40);
}
Демонстрация работы последнего примера:
Полезные страницы #
- Набор GyverKIT – наш большой стартовый набор Arduino, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])
- Поддержать автора за работу над уроками