С одной стороны, учимся писать библиотеку на C++ для Arduino, с другой стороны, пишем очень полезную, маленькую библиотеку для LED дисплея 8x8 с микросхемой MAX7219.

Благодаря такой постановке задачи, библиотека будет максимально понятной и ничего лишнего.

Название библиотеки MaxAndMatrix следует из назначения библиотеки. Микросхема MAX7219 может управлять 8-ю семи-сегментными светодиодными индикаторами или светодиодной матрицей 8х8 светодиодов. У нас дисплей из микросхемы MAX и светодиодная матрица, отсюда и название библиотеки MaxAndMatrix.

Создадим файл MaxAndMatrix.h. Файл библиотеки с расширением h, обычно содержит определения классов и прототипы функций.

#ifndef MaxAndMatrix_h
#define MaxAndMatrix_h
#include <Arduino.h>

class MaxAndMatrix {
  private :
    int din;
    int clk;
    int cs;
    int nturns = 0;                                 // Число поворотов на 90 против часовой
  public:
    MaxAndMatrix(int dinPin, int clkPin, int csPin);
    void begin(bool testMatrix = 0);                // зажечь все светодиоды если testMatrix = 1
    void loadByte(int addr, byte data = 0);         // загрузить одну строку addr = [0...7]
    void setRotation(int numberOFturns);            // установить число поворотов матрицы на 90
    void loadMatrix(byte matrix[]);                 // загрузить все 8 регистров LED матрицы
    void force(int powerOFlight);                   // яркость powerOFlight = [0...15]
    void onOff(bool HighLow);                       // включить/выключить LED
    void clear(void);                               // очистить все 8 регистров LED матрицы
};

#endif	//MaxAndMatrix.h

Скетч 1. Файл MaxAndMatrix.h

В файле MaxAndMatrix.h объявлен класс MaxAndMatrix. Внутри этого класса будут использованы несколько переменных и будет создано несколько функций (методов) для управления свойствами и поведением объектами класса MaxAndMatrix. Вместо функций, в файл с расширением h, обычно помещают только прототипы функций, их заголовки (заготовки).

Пишем файл MaxAndMatrix.cpp, в нём функции наполняем телами.

#include "MaxAndMatrix.h"

//      Register    Address-1
#define DECODEMODE  0x9-1
#define INTENSITY   0xA-1
#define SCANLIMIT   0xB-1
#define SHUTDOWN    0xC-1
#define DISPLAYTEST 0xF-1

MaxAndMatrix:: MaxAndMatrix(int dinPin, int clkPin, int csPin) {
  din = dinPin;
  clk = clkPin;
  cs = csPin;
}

void MaxAndMatrix::loadByte(int addr, byte data = 0) {       // загрузка байта в регистры микросхемы MAX7219
  digitalWrite(cs, LOW);
  shiftOut(din, clk, MSBFIRST, addr + 1);   // В MAX7219 нумерация аресов регистров строк LED начинается с 1
  shiftOut(din, clk, MSBFIRST, data);
  digitalWrite(cs, HIGH);
}

void MaxAndMatrix::setRotation(int numberOFturns) {
  nturns = numberOFturns % 4;               // Вращаем только 3 раза. numberOFturns=0 без вращения
}                                           // 1 на 90° против часовой стрелки, 2 на 180°, 3 на 270°

void MaxAndMatrix::loadMatrix(byte matrix[]) {
  byte rmatrix[8];                          // вспомогательная матрица.
  byte zmatrix[8];                          // копия матрицы пользователя, пользовательскую не портим.
  
  for (int i = 0 ; i < 8; i++) {
    zmatrix[i] = matrix[i];                 // создание копии матрицы пользователя
  }
  for (int r = 1; r <= nturns; r++) {       // вращение матрицы nturns раз
    for (int i = 0; i < 8; i++) {           // вращение на 90° против часовой стрелки один раз
      rmatrix[i] = 0;
      for (int j = 0; j < 8; j++) {
        rmatrix[i] |= ((zmatrix[7 - j] & (1 << i)) >> i) << j;
      }
    }
    for (int i = 0; i < 8; i++) {
      zmatrix[i] = rmatrix[i];              // копирование повёрнутой матрицы в исходную
    }                                       // после каждого поворота
  }
  for (int i = 0 ; i < 8; i++) {
    loadByte(i, zmatrix[i]);                // загрузка матрицы в микросхему MAX7219
  }
}

void MaxAndMatrix::clear(void) {
  for (int i = 0 ; i < 8; i++) {
    loadByte(i, 0);                         // загрузка 0 в регистры микросхемы MAX7219 по адресам LED
  }
}

void MaxAndMatrix::begin(bool testMatrix = 0) {
  pinMode(din , OUTPUT);
  pinMode(clk, OUTPUT);
  pinMode(cs, OUTPUT);
  clear();                                  // очистить LED регистры микросхемы MAX7219
  loadByte(DECODEMODE, 0);                  // 0 для матриц, остальное для семисегментных индикаторов
  loadByte(INTENSITY, 7);                   // средняя яркость
  loadByte(SCANLIMIT, 7);                   // количество рядов в LED матрице
  loadByte(SHUTDOWN , 1);                   // включить матрицу
  loadByte(DISPLAYTEST, 0);                 // запретить тест LED матрицы
  if (testMatrix) {                         // тестировать LED матрицу 3сек. если begin(1)
    loadByte(DISPLAYTEST, 1);
    delay(3000);
    loadByte(DISPLAYTEST, 0);
  }
}

void MaxAndMatrix::force(int powerOFlight) {
  loadByte(INTENSITY, powerOFlight);        // меняем яркость свечения светодиодов powerOFlight=[0...15]
}

void MaxAndMatrix::onOff(bool HighLow) {
  loadByte(SHUTDOWN, HighLow);              // включить/выключить LED матрицу
}

Скетч 2. Файл MaxAndMatrix.cpp. 

Функция begin содержит инструкции для микросхемы MAX7219. Подробнее см. в статье MAX7219 и Arduino

В библиотеке MaxAndMatrix функция loadMatrix() получилась избыточно сложной, но в ней мы позаботились о повороте изображения на угол 0°, 90°, 180° и 270°, не изменяя содержимое матрицы (массива) пользователя. Такой поворот изображения, скорее всего, пригодится в дальнейшем, ведь ориентирует LED матрицу в пространстве конструктор, а генерирует изображение программист. Чтобы программы были универсальными и чтобы любые дисплеи работали с нашей программой мы добавили возможность предварительно настроенного с помощью функции setRotation() поворота изображения на угол 0°, 90°, 180° или 270° в момент вывода на LED матрицу.

Пишем пример, с использованием библиотеки MaxAndMatrix в Arduino IDE.

#include "MaxAndMatrix.h"

int dataPin = 12;                // DIN
int clockPin = 11;               // CLK
int csPin = 10;                  // LOAD (CS)
MaxAndMatrix led88 = MaxAndMatrix(dataPin, clockPin, csPin);

byte matrix[8] = {0b01110001,
                  0b11011011,
                  0b10001110,
                  0b10001100,
                  0b10001000,
                  0b11111111,
                  0b10000001,
                  0b11111111
                 };

void setup() {
  led88.begin(0);
  led88.force(0);
}

void loop() {
  for (int i=0; i<4; i++) {
  led88.setRotation(i);
  led88.loadMatrix(matrix);
  delay(700);
  }
}

Скетч 2. Файл RotateR.ino

В программе скетч 2, массив matrix[8] содержит битовый образ изображения буквы R, построенный как показано на рисунке 1.

Рис. 1. Битовый образ изображения буквы R.

С таким же успехом массив matrix[8] можно было построить как показано на рисунке 2, библиотека MaxAndMatrix позволяет нам это делать.

Рис. 2. Битовый образ изображения буквы R.

Сохраняем все файлы проекта в папке RotateR. Arduino IDE сохранила файл RotateR.ino в этой папке, а мы добавили туда файлы библиотеки MaxAndMatrix.h и MaxAndMatrix.cpp.

Закройте все окна Arduino и опять откройте программу RotateR. Теперь в Arduino IDE доступны для редактирования все три файла проекта.

Рис. 3.  Arduino IDE.

Собираем схему и тестируем ПО.

Рис. 4. Принципиальная электрическая схема LED дисплея 8x8 с микросхемой MAX7219 управляемого микроконтроллером ATtiny88.

Рис. 5. Макет микроконтроллер ATtiny88 и LED дисплей 8x8 с микросхемой MAX7219.

Ещё один пример использования библиотеки MaxAndMatrix, игра пинг-понг.

/*
 * Pong a ping-pong game for one person
 * This is my version of the ping-pong program
 * 
 * Created on august 24, 2021.
 * Author of this program code : Diorditsa A.
 * I thank Sergey Polozkov for checking the code for hidden errors
 * 
 * pong.ino is distributed in the hope that it will be useful, but
 * WITHOUT WARRANTY OF ANY KIND; not even an implied warranty
 * MARKETABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See. See the GNU General Public License for more information.
 * You can get a copy of the GNU General Public License
 * by link http://www.gnu.org/licenses/
 */


#include "MaxAndMatrix.h"

int dataPin = 12;                // DIN
int clockPin = 11;               // CLK
int csPin = 10;                  // LOAD (CS)
MaxAndMatrix led88 = MaxAndMatrix(dataPin, clockPin, csPin);
byte matrix[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int xBall, yBall, temp , vx, vy, racket;
int k1pin = 6; int k2pin = 5;    // Кнопки, замыкающие на минус

void setup() {
  pinMode(k1pin, INPUT_PULLUP);
  pinMode(k2pin, INPUT_PULLUP);
  led88.begin(0);
  led88.force(0);
  led88.setRotation(1);
  led88.loadMatrix(matrix);
  xBall = 3; yBall = 0; vx = 1; vy = 1; temp = 250;
  racket = 2;
}

void loop() {
  if (!digitalRead(k1pin) and racket < 6) {
    racket++;
  }
  if (!digitalRead(k2pin) and racket > 0) {
    racket--;
  }
  matrix[7] = 0;
  matrix[yBall] = 1 << xBall;
  matrix[7] |= B11 << racket;
  led88.loadMatrix(matrix);
  matrix[yBall] = 0;
  delay (temp);

  if (yBall == 6) {
    if (xBall == racket or xBall == racket + 1) {
      vy = -1;
    }
    if (xBall == racket - 1 and vx == 1) {
      vy = -1; vx = -1;
    }
    if (xBall == racket + 2 and vx == -1) {
      vy = -1; vx = 1;
    }
  }
  if (yBall == 7) {
    yBall = -1;
  }
  if (xBall == 0) {
    vx = 1;
  }
  if (xBall == 7) {
    vx = -1;
  }
  yBall += vy;  xBall += vx;
  if (yBall == 0) {
    vy = 1;
  }
}

Скетч 4. Файл Pong.ino