LedMatrix -- это гибкая и мощная Arduino-C++11 библиотека для управления светодиодными матрицами на чипах MAX7219 и MAX7221.
С помощью библиотеки LedMatrix вы можете гибко управлять светодиодными матрицами подключенными с помощью чипов MAX7219 и MAX7221. А так же каскадами таких матриц. И целыми группами каскадов. Она может работать как через программный SPI, используя для подключения любые три свободные пина, так и через аппаратный интерфейс SPI. Аппаратный SPI, естественно быстрее.
Теперь по порядку.
Прежде всего, необходимо подключить заголовочный файл: #include <LedMatrix.h>
.
Далее, как это принято в программах для Ардуино, необходимо создать глобальный объект:
#include "LedMatrix.h"
// pin 11 is connected to the DataIn
// pin 13 is connected to the CLK
// pin 10 is connected to LOAD (cs)
// Software-SPI
LedMatrix matrix(11, 13, 10);
или так:
#include "LedMatrix.h"
//Hardware-SPI
LedMatrix matrix(10);
Теперь с матрицей можно работать.
Предусмотрено два конструктора.
Один конструктор создает матрицу, работающую через программный интерфейс SPI:
// Software-SPI Constructor
// @param dataPin pin on the Arduino where data gets shifted out (DIN)
// @param clockPin pin for the clock (CLK)
// @param csPin pin for selecting the device (CS - chip select pin)
LedMatrix(uint8_t data, uint8_t clk, uint8_t cs);
другой через аппаратный:
// HardWare-SPI Constructor
// @param csPin pin for selecting the device (CS -- chip select pin)
LedMatrix(uint8_t cs);
Выбор используемого интерфейса SPI зависит от вызванного конструктора.
Перечислю доступные методы.
// Set the shutdown (power saving) mode for the device
void shutdown() const;
Метод shutdown()
отключает питание матрицы, в целях энергосбережения. По умолчанию, при старте, питание матрицы включено.
// Set the wakeup mode for the device
void wakeup() const;
Метод wakeup()
включает питание матрицы, если оно было ранее выключено.
// Set the brightness of the display.
// @param intensity the brightness of the display. (0..15)
void setIntensity(uint8_t intensity) const;
Метод setIntensity()
устанавливает яркость светодиодов. Возможные значения от 0 до 15.
// Switch all LEDs on the display to off.
void clear();
Метод clear()
"очищает" экран, выключая все точки матрицы.
// Switch all LEDs on the display to on.
void fill();
Метод fill()
"заполняет" экран, включая все точки матрицы.
Как я уже говорил, матрицы могут быть объединены в каскад. Я считаю, что при их объединении в каскад нужно исходить в первую очередь из удобства монтажа. В этом случае, некоторые матрицы могут оказаться повернутыми или даже перевернутыми. На этот случай, я добавил возможность программного поворота матриц.
Следующие методы реализуют эту возможность:
// Set how many times to rotate the matrix clockwise
// @param From 0 to 3
void setRotation(uint8_t times = 1);
// Reset rotation flag to default
void resetRotation();
// Get how many times the matrix was rotated clockwise
uint8_t getRotation() const;
Узнать, какой индекс имеет матрица в каскаде поможет следующий метод:
// get device index in cascade
uint16_t index() const;
Теперь о том как "заполнять" матрицы.
Для заполнения матрицы доступны следующие методы:
// Set the status of a single LED.
// @param Row row the row of the Led (0..7)
// @param Col col the column of the Led (0..7)
// @param state If true the led is switched on, if false it is switched off
void set(const Row &row, const Col &col, bool state);
// Turn on LED at a point
// @param Row row the row of the Led (0..7)
// @param Col col the column of the Led (0..7)
void on(const Row &row, const Col &col);
// Turn off LED at a point
// @param Row row the row of the Led (0..7)
// @param Col col the column of the Led (0..7)
void off(const Row &row, const Col &col);
// Set all LEDs in a row to a new state
// @param row which is to be set (0..7)
// @param value each bit set to 1 will light up the corresponding LED.
void set(const Row &row, buint8_t value);
// Set all LEDs in a column to a new state
// @param col -- column which is to be set (0..7)
// @param value -- each bit set to 1 will light up the corresponding LED.
void set(const Col &col, buint8_t value);
// Set all LEDs in a row to a new state
// @param row which is to be set (0..7)
// @param value each bit set to 1 will light up the corresponding LED.
void setRow(const Row &row, buint8_t value);
// Set all LEDs in a column to a new state
// @param col -- column which is to be set (0..7)
// @param value -- each bit set to 1 will light up the corresponding LED.
void setCol(const Col &col, buint8_t value);
// Allows to initialize the values of all points of the matrix
// @param initializer_list instance
template <typename T>
void set(const std::initializer_list<T> &disp);
// Allows to initialize the values of all points of the matrix
// Attention. If you pass an array to this function, strictly follow its length
// @param raw array
void set(const uint8_t arr[]);
В списках аргументов вы можете видеть здесь типы Row
, Col
и buint8_t
.
Не пугайтесь. Они были введены для удобства. Чем они могут быть вам полезны я напишу ниже.
А пока вам достаточно знать, что эти типы автоматически преобразуются в числа типа uint8_t
и обратно.
По сути, эти типы и есть uint8_t
+ немного сахара.
То есть такая запись:
matrix.on(3, 5);
абсолютно корректна.
Расписывать назначения всех сеттеров не буду, поскольку их называния и прототипы говорят сами за себя. Подробно остановлюсь на двух.
template <typename T>
void set(const std::initializer_list<T> &disp);
Этот метод позволяет вам заполнить матрицу на месте. Прямо во время компиляции, не создавая промежуточных массивов или чего то еще. Вот пример использования:
matrix.set({0b00000000, 0b01100110, 0b10011001, 0b10000001, 0b01000010, 0b00100100, 0b00011000, 0b00000000});
Метод void set(const uint8_t arr[])
позволяет заполнить матрицу предварительно созданным массивом:
uint8_t arr[8] = {0b00000000, 0b00100000, 0b00000000, 0b01100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000};
matrix.set(arr);
Для извлечения информации из матрицы предназначена следующая группа методов:
// Get state of LED point on matrix
// @param row the row of the Led (0..7)
// @param col the column of the Led (0..7)
bool get(const Row &row, const Col &col) const;
// Get the values on row of LED-matrix
// @param row the row of the Led (0..7)
buint8_t get(const Row &row) const;
// Get the values on colomn of LED-matrix
// @param col the column of the Led (0..7)
buint8_t get(const Col &col) const;
// Get the values on row of LED-matrix
// @param row the row of the Led (0..7)
buint8_t getRow(const Row &row) const;
// Get the values on colomn of LED-matrix
// @param col the column of the Led (0..7)
buint8_t getCol(const Col &col) const;
Я думаю, дополнительные комментарии не нужны.
Точки матрицы, а так же отдельные строки и столбцы можно инвертировать. Для этого предназначены следующие методы:
// Invert all points of matrix
void invert();
// Invert current point on matrix
// @param row the row of the LED (0..7)
// @param col the column of the LED (0..7)
void invert(const Row &row, const Col &col);
// Invert row on matrix
// @param row the row of the LED (0..7)
void invert(const Row &row);
// Invert colomn on matrix
// @param col the column of the LED (0..7)
void invert(const Col &col);
// Invert row on matrix
// @param row the row of the LED (0..7)
void invertRow(const Row &row);
// Invert colomn on matrix
// @param col the column of the LED (0..7)
void invertCol(const Col &col);
// Shift matrix
// @param value is shifting value
// @return shifted value
buint8_t shiftUp(buint8_t value = 0);
buint8_t shiftDown(buint8_t value = 0);
buint8_t shiftLeft(buint8_t value = 0);
buint8_t shiftRight(buint8_t value = 0);
Эти методы сдвигают матрицу в ту или иную сторону. Возвращаемым значением является вымещенная строка или столбец. В качестве аргумента можно передать значение замещаемой строки или столбца.
Пару слов о типах Row
, Col
и buint8_t
.
Row
, Col
объявлены в заголовочном файле RowCol.h
. Оба эти типа могут быть использованы как числовые, но обладают дополнительными возможностями.
Переменные Row
, Col
всегда в диапазоне 0..7.
Они выполняют роль итератора и позволяют осуществить красивую перегрузку. То есть вместо неуклюжего кода
uint8_t foo(/*...*/) {/*...*/}
for(uint8_t row = 0; row < 8; ++row) {
matrix.setRow(row, foo(row));
}
можно написать лаконичный код:
uint8_t foo(/*...*/) {/*...*/}
for(auto &row: matrix.rows()) {
matrix.set(row, foo(row));
}
Для их использования предусмотрено два метода:
// Make rows and colomns iterable
RowsIterator rows() const;
ColsIterator cols() const;
которые возвращают итераторы для строк и столбцов, соответственно.
Тип buint8_t
определен в заголовочном файле BitInt.h
.
Его определение это просто специализация шаблонного класса BitInt
:
// Types predefinition
using buint8_t = BitInt<uint8_t>;
// ...
Он ведет себя как uint8_t, но позволяет легко получить доступ к своему бинарному представлению.
buint8_t x = 88; // 01011000
x[2] = 1; // 01111000
x[3] = false; // 01101000
bool a = x[4]; // true
bool a = x[7]; // false
// Iteration:
for(auto v: x) {
Serial.print(v ? "{I}" : "{O}");
}
Матрицы могут быть объеденены в каскад. Схема подключения вот такая:
-> VVC -> VVC ->
-> GND -> GND ->
-> DIN DOUT ->
DOUT -> DIN
-> CS -> CS ->
-> CLK -> CLK ->
Как и одиночные матрицы, каскад матриц может управляться как с помощью программного интерфейса SPI, так и с помощью аппаратного. Точно так же, программный SPI позволяет использовать любые три свободные пина, аппаратный оставляет свободным только один пин (CS):
// Hardware-SPI wiring scheme:
// CLK => SCLK (Arduino UNO/Nano/Mini pin 13)
// DIN => MOSI (Arduino UNO/Nano/Mini pin 11)
// CS => (Arduino any pin)
но аппаратный SPI заметно шустрее.
Software-SPI:
#include <MatrixCascade.h>
// pin 11 is connected to the DataIn
// pin 13 is connected to the CLK
// pin 10 is connected to LOAD (cs)
const uint8_t CascadeSize = 3;
// Software-SPI
MatrixCascade<CascadeSize> cascade(11, 13, 10);
Hardware-SPI:
#include <MatrixCascade.h>
// pin 11 is connected to the DataIn
// pin 13 is connected to the CLK
// pin 10 is connected to LOAD (cs)
const uint8_t CascadeSize = 3;
// HardWare-SPI
MatrixCascade<CascadeSize> cascade(10);
Обратите внимание, класс MatrixCascade
шаблонный. И вам нужно явно указать размер каскада (MatrixCascade<3>
) на этапе компиляции.
Что бы использовать каскады матриц и группы каскадов матриц, подключите заголовочный файл MatrixCascade.h
Уже знакомые нам методы, которые в данном случае являются групповыми:
// Set the shutdown (power saving) mode for all devices
void shutdown() const;
// Set the wakeup mode for all devices
void wakeup() const;
// Set the brightness of all displays.
// @param intensity the brightness of the display. (0..15)
void setIntensity(uint8_t intensity) const;
// Switch all LEDs on all displays to off.
void clear();
// Switch all LEDs on all displays to on.
void fill();
// Invert all points of all matrixes
void invert();
// How many times to rotate all matrixes clockwise
// @param From 0 to 3
void setRotation(uint8_t times = 1);
// Reset rotation flag for all matrixes to default
void resetRotation();
Метод позволяющий получить размер каскада:
// Returns the number of devices on this MatrixCascade
constexpr uint16_t size() const;
Получение доступа к матрице по индексу:
LedMatrix& get(uint16_t index);
Класс MatrixCascade
обладает свойствами массива. В частности доступ к содержащимся в нем матрицам можно получить через оператор []
:
cascade[0].setRotation(3);
cascade[1].setRotation(1);
А так же MatrixCascade
является итерируемым:
for(auto &matrix: cascade) {
matrix.shiftUp();
}
Каскады матриц можно, в свою очередь, объединять в суперкаскады.
Суперкаскад от каскада отличается только способом конструирования объекта. На самом деле это все тот же MatrixCascade
.
Что бы создать суперкаскад, нужно использовать функцию combineCascades()
. Пример:
auto cascade = combineCascades(
MatrixCascade<5>(10),
MatrixCascade<8>(12),
MatrixCascade<7>(1, 2, 3),
MatrixCascade<8>(4, 5, 6),
MatrixCascade<8>(7, 8, 9),
MatrixCascade<3>(14),
MatrixCascade<6>(15),
);
Переменная cascade
будет объектом типа MatrixCascade<45>
. Соответственно она будет управлять 45-ю матрицами. Это позволяет снять ограничения 8 матриц на каскад, накладываемые чипами MAX7219 и MAX7221. Фактическим ограничением является количество свободных пинов.
Более подробную информацию можно получить в исходном коде, который я постарался снабдить комментариями, и в примерах.
Библиотека не реализует средств для печати текстовых строк на каскаде матриц. Это сделано специально. Поскольку матрицы могут быть смонтированы произвольным образом. И внесение в библиотеку кода предполагающего какой-то конкретный вид монтажа было бы нарушением целостности. Тем более написать надстроечную библиотеку с нужной функциональностью не составит труда.
Пара примеров как сделать бегущую строку: 1, 2
Первоначально, библиотека была форком библиотеки LedControl. Я полностью переработал исходную библиотеку. Целю было получить более гибкий интерфейс, оптимизировать читаемость кода и производительность. В настоящий момент от исходной библиотеки осталась всего пара строк, так что проект был вынесен в отдельный репозитарий.
Не стесняйтесь сообщать об найденных ошибках и присылать свои предложения.
Если вы хорошо знаете английский и в каком-то месте мой перевод показался вам корявым, feel free to report it.