Управление матрицей 8х8

Наконец доехали из поднебесной матричные модули. Каждый модуль состоит из микросхемы MAX7219 (), светодиодной матрицы, в обвязке стоят один конденсатор и один резистор.


Управляется MAX7219 по интерфейсу SPI.


Микросхемы в кластере соединены последовательно. Читал в интернете, что максимально возможное последовательное подключение допускает всего 8 штук MAX7219. Не верьте. 16 модулей соединил, и все прекрасно работает.

Модули, представленные на Али, бывают в нескольких вариантах исполнения. Наибольшей популярностью пользуются 2 вида: с микросхемой в DIP и в SOIC корпусах. Модуль с DIP-микросхемой большего размера и не так удобен при соединении в кластер. Соединять придется кучей проводов.


Модули с микросхемой в SOIC-корпусе имеют размер светодиодной матрицы и соединяются пайкой или джамперами. Получается красиво и аккуратно.


Наиболее известными библиотеками для работы с матрицами и кластерами являются MAX72xx Panel от Марка Райса и Parola от MajicDesigns : первая библиотека проще в использовании, вторая посложнее с бОльшими возможностями. Распишу подробнее.

MAX72xx Panel

При использовании MAX72xx Panel обязательна установка библиотеки Adafruit GFX .

Для русификации текста необходимо будет скачать ЭТОТ ФАЙЛ и заменить стандартный файл glcdfont.c в каталоге Arduino/Libraries/Adafruit-GFX-Library-master. Также в этом файле описаны, кроме нужных букв и цифр, куча всяких символов. Далеко не все они могут пригодиться. Картинка ниже поясняет как формируются символы.

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

Итак, библиотеки MAX72xx Panel и Adafruit GFX установлены, файл glcdfont.c заменен. Запускаем Arduino IDE, открываем ФАЙЛ . В скетче есть функция utf8rus. Она обеспечивает перекодировку таблицы символов для русского языка. Она нужна только для нормального вывода из программы, то есть в программе нужный текст пишется на русском. Если текст вводится через СОМ-порт, то коррекция кодов символа происходит в функции Serial_Read. В IDE и в консоли разработчики использовали разные кодировки.

В начале файла присутствуют строки необходимые для работы библиотеки.

int numberOfHorizontalDisplays = 1;

int numberOfVerticalDisplays = 16;

У меня модули с микросхемой в SOIC-корпусе. У них есть небольшая особенность. Матрица у модулей установлена повернутой на 90 градусов. Это плата за удобство соединения. Если запустить скетчи, идущие в комплекте с библиотеками, они будут выводить текст снизу вверх в каждом модуле. Текст будет выводится зигзагами. Для лечения этого недуга библиотеке надо "сказать", что вертикальных дисплеев 16 (физически они расположены горизонтально). И потом в void Setup указать библиотеке строку

matrix.setRotation(matrix.getRotation() + 1);

Она программно перевернет каждую матрицу. И отображаться все будет нормально.

У модулей с DIP-корпусом микросхем такого нет. Там все красиво, кроме кучи проводов.

Библиотека MAX72xx Panel довольно скромная. Визуальных эффектов вывода нет. Кластер воспринимается как одно целое. Намного лучше дела обстоят с MD Parola.

Parola от MajicDesigns.

Обладатели модулей с микросхемой в SOIC-корпусе также столкнутся с проблемой ориентации модулей в кластере. Только выглядит это немного по-другому нежели в MAX72xx. Здесь модули окажутся как бы не в своей очереди.


Скетч HelloWorld из образцов в комплекте с библиотекой.

Программно в скетче мне не удалось вылечить этот недуг. Я вылечил его по-другому. В файле Adruino/libraries/MD_MAX72xx_lib.h в конце нужно найти строки как на картинке.


И исправить в выделенной строке выделенную 1 на 0. Сохранить файл. Arduino IDE можно не перезагружать. Заливаем, смотрим.


Теперь можно использовать 24 эффекта анимации. Анимация запускается командой P.displayText(«Текст для вывода», «выравнивание текста», скорость, задержка показа, эффект появления, эффект исчезновения). Как видите, настроек достаточно много.

И самый смак - деление кластера на виртуальные зоны. С зонами работать не очень сложно. Скетч не выкладываю, он есть в образцах, идущих с библиотекой. Теперь выводить часы в начале кластера и бегущую строку с новостями на оставшихся модулях можно без проблем, почти.

Как вы уже догадываетесь проблема с кириллическими буквами. Она тоже решаема. Рядом с предыдущим файлом в той же директории лежит файлик MD_MAX72xx_font.cpp. Это файл шрифта. Символы в нем сформированы аналогично файлу шрифта GFX библиотеки. Есть небольшое отличие. Здесь размер символа может быть меньше 5 точек. В библиотеке Adafruit GFX восклицательный знак, например, занимает также 5 точек шириной, как и любой другой символ, только используется один ряд точек. Остальные не светятся, но используются под символ. В Parola тот же восклицательный знак занимает также один ряд точек, только рядом не пустые точки, а могут стоять соседние символы. Понятнее будет разобраться по картинке.


Дополнить файл кириллическими символами аналогично файлу из первой рассмотренной библиотеки пока времени нет. Если кто-нибудь это сделает и пришлет мне файл, я добавлю его к этой статье, и Вам будут благодарны и я, и гости моего сайта.

Итог. Библиотека MAX72xx Panel от Марка Райса проста в использовании и понимании, но с бедным функционалом.

Библиотека Parola от MajicDesigns посложнее и ее возможностей хватит практически для любого применения.

Матричный дисплей - устройство состоящее из LED матрицы 8x8 и платы управления на базе микросхемы MAX7219 c минимальной необходимой обвязкой. На фото вы уже видети готовое устройство, но мне оно пришло по почте вот в таком виде, так что пришлось взять в руки паяльник и спаять всё в нужном виде.

Базовое подключение к плате Arduino Nano показано на принципиальной схеме.

Как видно из схемы - всё просто, нужно лишь соединить проводами контакты обеих плат в следующем порядке:

Arduino Nano Matrix 8x8
PIN 12 PIN DIN
PIN 11 PIN CLK
PIN 10 PIN CS
PIN 5V PIN 5V
PIN GND PIN GND

Итак, наконец-то пришло время поработать с кодом, и для того чтобы запустить LED матрицу, нужно скачать и установить библиотеку LedControl. Библиотека поддерживает работу с микросхемами MAXIM 7221 и 7219. LED матрица в качестве драйвера использует MAX7219, это то что нам нужно. Я думаю что нужно начать с небольшого скетча, демонстрирующего базовые функции данной библиотеки. В скетче, для включения/ выключения светодиодов использована функция setRow(). Синтаксис вызова функции setRow():

matrix.setRow(0, row, col, set);

Где matrix - объект класса LedControl

0 - адрес устройства на шине SPI, в данном случае устройство одно, и нумерация начинается с нулевого адреса

row - ряд(Возможные значения от 0 до 7)

col - колонка(Возможные значения от 0 до 7)

set - значение установки(true - включить, false - выключить)

matrix.setRow(0, 1, 4, true)

Включить на матрице светодиод, находящийся в 1-м ряду и в 4-м столбце.

Еще пример:

int x = 1;

int y = 4;

matrix.setRow(0, x, y, true);

matrix.setRow(0, x, y, false);

То же самое только с использованием переменных, и последующим выключением зажженного светодиода по заданным координатам. Итак, в скетче проигрывается несколько анимаций, с использованием функции setRow().

#include "LedControl.h" //Синтаксис создания класса LedControl(dataPin,clockPin,csPin,numDevices) //Где LedControl - объект класса //dataPin - пин на плате Arduino к которому будет подключен пин DIN //clockPin - пин на плате Arduino к которому будет подключен пин CLK //csPin - пин на плате Arduino к которому будет подключен пин CS //numDevices - количество устройств на шине //Создать объект класса matrix в нашем случае с одним подключенным устройством LedControl matrix = LedControl(12, 11, 10, 1); //Время задержки включения/выключения светодиода на матрице const int delayValue = 100; //Прототипы функций //Последовательное включение/выключение void PointSerialSet(); //Змейка справа - налево void PointRigthToLeft(); //Змейка снизу - вверх void PointDownToUp(); //Змейка слева - направо void PointLeftToRigth(); //Змейка сверху - вниз void PointUpToDown(); //Эффект - затухающий импульс void PointFading(); //Эффект - импульс void PointPulse(); //Эффект - нарастающий импульс void PointClimbing(); //Эффект закручивание спирали внутрь void PointToSpiralIn(); //Эффект раскручивание спирали наружу void PointToSpiralOut(); void setup() { //Устройству с адресом 0 выйти из спящего режима по умолчанию matrix.shutdown(0, false); //Установить яркость Led матрицы на 8 //Возможные значения от 0 до 15 matrix.setIntensity(0, 8); //Очистить дисплей matrix.clearDisplay(0); } void loop() { //Вызываем функции поочередно PointSerialSet(); PointRigthToLeft(); PointDownToUp(); PointLeftToRigth(); PointUpToDown(); PointFading(); PointPulse(); PointClimbing(); PointToSpiralIn(); PointToSpiralOut(); } //Тела функций void PointSerialSet() { //Используем функцию setLed(address, row, col, set) //Последовательное включение по рядам for(int i = 0; i < 8; i ++) { for(int j = 0; j < 8; j ++) { //Включить светодиод matrix.setLed(0, i, j, true); delay(delayValue); //Выключить светодиод matrix.setLed(0, i, j, false); } } } void PointRigthToLeft() { //Используем функцию setLed(address, row, col, set) //Змейка справа - налево for(int i = 7; i >= 0; i --) { for(int j = 7, n = 0; j >= 0, n < 8; j --, n ++) { if((i % 2) > 0) { matrix.setLed(0, j, i, true); delay(delayValue); matrix.setLed(0, j, i, false); } else { matrix.setLed(0, n, i, true); delay(delayValue); matrix.setLed(0, n, i, false); } } } } void PointDownToUp() { //Используем функцию setLed(address, row, col, set) //Змейка снизу - вверх for(int i = 7; i >= 0; i --) { for(int j = 7, n = 0; j >= 0, n < 8; j --, n ++) { if((i % 2) > 0) { matrix.setLed(0, i, n, true); delay(delayValue); matrix.setLed(0, i, n, false); } else { matrix.setLed(0, i, j, true); delay(delayValue); matrix.setLed(0, i, j, false); } } } } void PointLeftToRigth() { //Используем функцию setLed(address, row, col, set) //Змейка слева - направо for(int i = 0; i < 8; i ++) { for(int j = 7, n = 0; j >= 0, n < 8; j --, n ++) { if((i % 2) > 0) { matrix.setLed(0, j, i, true); delay(delayValue); matrix.setLed(0, j, i, false); } else { matrix.setLed(0, n, i, true); delay(delayValue); matrix.setLed(0, n, i, false); } } } } void PointUpToDown() { //Используем функцию setLed(address, row, col, set) //Змейка сверху - вниз for(int i = 0; i < 8; i ++) { for(int j = 7, n = 0; j >= 0, n < 8; j --, n ++) { if((i % 2) > 0) { matrix.setLed(0, i, n, true); delay(delayValue); matrix.setLed(0, i, n, false); } else { matrix.setLed(0, i, j, true); delay(delayValue); matrix.setLed(0, i, j, false); } } } } void PointFading() { //Используем функцию setLed(address, row, col, set) //Эффект затухания int upValue = 0; int downValue = 7; for(int i = 0; i < 8; i ++) { if(i % 2) { for(int n = downValue; n >= upValue; n --) { matrix.setLed(0, n, i, true); delay(delayValue); matrix.setLed(0, n, i, false); } downValue --; } else { for(int j = upValue; j < downValue + 1; j ++) { matrix.setLed(0, j, i, true); delay(delayValue); matrix.setLed(0, j, i, false); } upValue ++; } } } void PointClimbing() { //Используем функцию setLed(address, row, col, set) //Эффект затухания int upValue = 4; int downValue = 4; for(int i = 0; i < 8; i ++) { if(i % 2) { for(int n = downValue; n >= upValue; n --) { matrix.setLed(0, n, i, true); delay(delayValue); matrix.setLed(0, n, i, false); } downValue ++; } else { for(int j = upValue; j < downValue + 1; j ++) { matrix.setLed(0, j, i, true); delay(delayValue); matrix.setLed(0, j, i, false); } upValue --; } } } void PointPulse() { //Используем функцию setLed(address, row, col, set) //Эффект пульса for(int i = 0; i < 8; i ++) { if(i == 4) { for(int climb = i; climb >= 0; climb --) { matrix.setLed(0, climb, i, true); delay(delayValue / 4); matrix.setLed(0, climb, i, false); } for(int fade = 1; fade < 8; fade ++) { matrix.setLed(0, fade, i, true); delay(delayValue / 4); matrix.setLed(0, fade, i, false); } } else { matrix.setLed(0, 4, i, true); delay(delayValue); matrix.setLed(0, 4, i, false); } } } void PointToSpiralIn() { //Используем функцию setLed(address, row, col, set) //Эффект спирали внутрь int lengthX = 8; // Ширина матрицы int lengthY = 8; // Высота матрицы int pointX = 0; int pointY = 0; int dir = 0; // Направление (0 - вправо, 1 - вниз, 2 - влево, 3 - вверх) int offset = 0; // Смещение // Перебираем всю матрицу for (int i = 0; i < lengthX * lengthY; i++) { matrix.setLed(0, pointY, pointX, true); delay(delayValue); matrix.setLed(0, pointY, pointX, false); if(dir == 0) { pointX ++; if(pointX >= lengthX - 1 - offset) { dir = 1; } continue; } if(dir == 1) { pointY ++; if(pointY > <= offset) { dir = 3; } continue; } if(dir == 3) { pointY --; if(pointY <= offset + 1) { dir = 0; offset ++; pointY = offset; } continue; } } } void PointToSpiralOut() { //Используем функцию setLed(address, row, col, set) //Эффект спирали внаружу int lengthX = 8; // Ширина матрицы int lengthY = 8; // Высота матрицы int pointX = 3; //начать с этих координат int pointY = 3; //начать с этих координат int dir = 0; // Направление (0 - вправо, 1 - вниз, 2 - влево, 3 - вверх) int offset = 3; // Смещение // Перебираем всю матрицу for (int i = 0; i < lengthX * lengthY; i++) { matrix.setLed(0, pointY, pointX, true); delay(delayValue); matrix.setLed(0, pointY, pointX, false); if(dir == 0) { pointX ++; if(pointX >= lengthX - 1 - offset) { dir = 1; } continue; } if(dir == 1) { pointY ++; if(pointY >= lengthY - 1 - offset) { dir = 2; } continue; } if(dir == 2) { pointX --; if(pointX <= offset - 1) { dir = 3; } continue; } if(dir == 3) { pointY --; if(pointY <= offset - 1) { dir = 0; offset --; pointY = offset; } continue; } } }

Видео работы скетча:


Пожалуйста, включите javascript для работы комментариев.

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

В принципе, пульт со всеми возложенными на него задачами справлялся вполне успешно . Но были и серьезные недостатки.

  1. Батарейки в корпус никак не влазили, поэтому приходилось их приматывать к корпусу изолентой:)
  2. Настройка параметров была вынесена на четыре потенциометра, которые оказались очень чувствительными к температуре. В помещении настраиваешь одни значения, выходишь на улицу - а они уже другие, уплыли.
  3. У Arduino Nano, которую я использовал в пульте, есть всего 8 аналоговых входов. Четыре были заняты настроечными потенциометрами. Один потенциометр служил газом. Два входа были подключены к джойстику. Оставался свободен только один выход, а параметров для настройки гораздо больше.
  4. Единственный джойстик был вовсе не пилотным. Управление газом с помощью потенциометра тоже весьма угнетало.
  5. А еще пульт не издавал никаких звуков, что иногда бывает крайне полезно.

Чтобы устранить все эти недостатки, я решил кардинально переделать пульт. И железную часть, и софт. Вот что мне захотелось сделать:

  • Сделать большой корпус, чтобы в него можно было запихнуть все что хочется сейчас (включая батарейки), и что захочется позже.
  • Как-то решить проблему с настройками, не за счет увеличения числа потенциометров. Плюс, добавить возможность сохранения параметров в пульте.
  • Сделать два джойстика, как на нормальных пилотных пультах. Ну и сами джойстики поставить православные.

Новый корпус

Идея чрезвычайно проста и эффективна. Вырезаем из оргстекла или другого тонкого материала две пластины и соединяем их стойками. Все содержимое корпуса крепится либо к верхней, либо к нижней пластине.

Элементы управления и меню

Чтобы управлять кучей параметров, нужно либо разместить на пульте кучу потенциометров и добавить АЦП, либо делать все настройки через меню. Как я уже говорил, настройка потенциометрами не всегда хорошая идея, но и отказываться от нее не стоит. Так что, решено было оставить в пульте четыре потенциометра, и добавить полноценное меню.

Чтобы перемешаться по меню, и менять параметры обычно используют кнопки. Влево, вправо, вверх, вниз. Но мне захотелось использовать вместо кнопок энкодер. Эту идею я подсмотрел у контроллера 3D-принтера.


Разумеется, за счет добавления меню, код пульта распух в несколько раз. Для начала я добавил всего три пункта меню: "Telemetry", "Parameters" и "Store params". В первом окне отображается до восьми разных показателей. Пока я использую только три: заряд батареи, компас и высота.

Во втором окне доступны шесть параметров: коэффициенты PID регулятора для осей X/Y,Z и корректировочные углы акселерометра.

Третий пункт позволяет сохранять параметры в EEPROM.

Джойстики

Над выбором пилотных джойстиков я долго не размышлял. Так получилось, что первый джойстик Turnigy 9XR я добыл у коллеги по квадрокоптерному делу - Александра Васильева, хозяина небезызвестного сайта alex-exe.ru . Второй такой же заказал напрямую на Hobbyking.


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

Питание

В старом пульте я использовал простой регулятор напряжения LM7805, который кормил связкой из 8 батареек AA. Жутко неэффективный вариант, при котором 7 вольт уходили на нагрев регулятора. 8 батареек - потому что под рукой был только такой отсек, а LM7805 - потому что в то время этот вариант мне представлялся самым простым, и главное быстрым.

Теперь же я решил поступить мудрее, и поставил достаточной эффективный регулятор на LM2596S. А вместо 8-ми AA батареек, установил отсек на два LiIon аккумулятора 18650.


Результат

Собрав все воедино, получился вот такой аппарат. Вид изнутри.


А вот с закрытой крышкой.


Не хватает колпачка на одном потенциометре и колпачков на джойстиках.

Наконец, видеоролик о том, как происходит настройка параметров через меню.


Итог

Физически пульт собран. Сейчас я занимаюсь тем, что дорабатываю код пульта и квадрокоптера, чтобы вернуть им былую крепкую дружбу.

По ходу настройки пульта, были выявлены недостатки. Во-первых, нижние углы пульта упираются в руки:(Наверное я немного перепроектирую пластины, сглажу углы. Во-вторых, даже дисплея 16х4 не хватает для красивого вывода телеметрии - приходится названия параметров сокращать до двух букв. В следующей версии девайса установлю точечный дисплей, либо сразу TFT матрицу.


Китай предоставляет за небольшую цену огромное количество не только электронных устройств, но и их компонентов. Небольшая матрица, составленная из светоизлучающих диодов может показывать нужную вам информацию, предположительно это числа, так как разрешение не очень большое, 8 на 8 светодиодов , каждый диаметром 3 мм . Эта матрица светит красным цветом, ведь он наиболее заметен и привлекает внимание больше, чем другие цвета.


Использовать для управления этой матрицы удобнее всего Arduino , ведь он цифровой. Если написать несложную программку вы сможете выводить нужную информацию на этот светодиодный "экранчик". В продаже существуют более дорогие трехцветные матрицы, в которых комбинируя основные три цвета вы можете создать почти любой.


Данная матрица имеет 16 выводов, выше предоставлена схема, где ясно показано как подключены элементарные составляющие матрицы - светодиоды, ориентируясь по ней вы сможете правильно подключить модуль для воспроизведения информации как цифровым, так и аналоговым устройством. Не забудьте про токоограничивающие резисторы, будет печально, если выгорит какой-то из пикселей. Номинальный продолжительный ток одной точки равен 20 мА , пиковый прямой ток 100 мА, напряжения питания 2,1-2,1 В , длина волны 625-630 нм , максимальная рассеиваемая мощность 40 мВт, общий здесь анод, то есть плюс. Габаритные размеры светодиодной матрицы 37 на 37 на 8 мм . Паять нужно аккуратно, не прикасайтесь паяльником с температурой 260 градусов более 5 с, рекомендуемая температура эксплуатации -40 - +70 по шкале Цельсия.

Если у вас есть несколько таких матриц на светодиодах вы можете соединять их, создавая большие по площади информационные табло.