Skip to contentSkip to main navigation Skip to footer

Arduino и матрица MAX7219

Описание


MAX7219 – микросхема для управления 7-сегментными индикаторами и матрицами 8х8. Благодаря встроенной динамической индикации и настройке тока этот чип в десятки раз упрощает работу и с теми, и с другими. На китайском рынке есть несколько вариантов исполнения матричных модулей на базе этой микросхемы:

В наборе GyverKIT можно встретить как первый, так и второй вариант слева. Подключаются и управляются они абсолютно одинаково, но первый вариант можно разобрать и отдельно поэкспериментировать с матрицей и микросхемой.

Подключение


Модуль подключается к питанию (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 как гораздо более лёгкую и быструю библиотеку. Библиотека идёт в архиве к набору GyverKIT, а свежую версию всегда можно установить/обновить из встроенного менеджера библиотек Arduino по названию GyverMAX7219. Краткая документация находится по ссылкам выше, базовые примеры есть в самой библиотеке.

  • GyverMAX7219 – базовое взаимодействие с  матрицей, возможность очистить и поставить точку по координатам
  • GyverGFX – вывод геометрии (линии, прямоугольники, окружности, кривые), вывод картинок из памяти, вывод текста (русский и английский шрифт нескольких размеров)

Объявление матрицы выглядит так:

//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);
}

Демонстрация работы примера с дисплеем:

Эти примеры есть в библиотеке, их можно открыть из Arduino IDE/Файл/Примеры/GyverMAX7219

Домашнее задание


  • Изучить остальные функции для работы с библиотекой

 

Связанные уроки

Полезный пример?

Подписаться
Уведомить о
14 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии