Подпишись и читай
самые интересные
статьи первым!

Злые глаза на матрице led 8х8. Масштабирование светодиодной матрицы

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

1. Матричный индикатор

Как мы уже знаем, сегментные индикаторы, будь то шкалы или цифры, состоят из отдельных светодиодов, соединенных вместе. Например, у группы светодиодов могут быть соединены все катоды. Такой индикатор имеет приписку «с общим катодом», в противном случае — «с общим анодом». А что будет, если мы разместим светодиоды не в виде цифры или шкалы, а в виде сетки? Получится уже вполне себе графический индикатор. То есть такой, на котором можно отобразить не только число, но и какое-то изображение. Такая сетка называется матричным индикатором, а в случае использования светодиодов — светодиодной матрицей. Разрешение матричного индикатора — это количество точек по горизонтали и вертикали. Например, самые распространенные индикаторы имеют разрешение 8×8 точек. Если требуется светодиодная матрица с большим разрешением, то её просто-напросто составляют из нескольких 8×8 индикаторов. Как это делать, мы увидим позже. А пока разберемся как соединяются все 64 светодиода внутри матрицы. Конечно, можно бы было как и в случае семисегментного индикатора соединить все светодиоды общим катодом или анодом. В этом случае нам бы потребовалось либо 64 вывода контроллера, либо 8 сдвиговых регистров. Оба варианта весьма расточительны. Более правильный вариант — объединить светодиоды в группы по 8 штук с общим катодом. Пусть это будут столбцы матрицы. Затем, параллельные светодиоды в этих столбцах объединить снова в группы по 8 штук уже с общим анодом. Получится вот такая схема:
Предположим, стоит задача зажечь светодиод R6C3. Для этого нам потребуется подать высокий уровень сигнала на вывод R6, а вывод C3 соединить с землей. Не выключая эту точку, попробуем зажечь другую — R3C7. Положительный контакт питания соединим с R3 и землю с C7. Но в таком случае строки R6 и R3 будут пересекаться с колонками C3 и C7 не в двух, а в четырех местах! Следовательно и зажжется не две, а четыре точки. Проблема! Очевидно, что помочь сможет всё та же . Если мы будем включать точки R6C3 и R3C7 по-очереди очень быстро, то сможем использовать персистентность зрения — способность интерпретировать быстро сменяющиеся изображения как одно целое.

2. Светодиодная матрица и сдвиговые регистры

В нашем уроке мы будем подключать к Ардуино Уно самую простую светодиодную матрицу 8×8 красного свечения. Нумерация выводов начинается с нижнего левого угла. При этом, нумерация ног 1-16 не связана никакой логикой с нумерацией колонок и строк C и R.
Ориентируясь на урок про динамическую индикацию, попробуем использовать в схеме управления матричным индикатором 8-битные сдвиговые регистры. Один регистр подключим к выводам индикатора, отвечающим за колонки, а второй к выводам строк. Принципиальная схема
Важное замечание №1. Необходимо, чтобы резисторы в этой схеме были на линиях, идущих от первого сдвигового регистра. Этот сдвиговый регистр отвечает за колонки. При таком подключении, каждый резистор будет задавать ток только для одного светодиода на каждом шаге динамического алгоритма. Следовательно, все светодиоды будут светиться равномерно. Важное замечание №2. Указанная выше схема носит сугубо ознакомительный характер. Правильнее будет включить в разрыв между вторым регистром и матрицей дополнительную силовую микросхему, например транзисторную сборку ULN2003.

3. Программа

Чтобы было веселей, попробуем высветить на индикаторе смайлик. Как уже было сказано, для вывода изображения на матрицу воспользуемся динамической индикацией. А именно, будем высвечивать нашу картинку построчно. Сначала зажжем нужные колонки в самой верхней строке, затем во второй, в третьей, и так все 8 строк. За колонки у нас будет отвечать первый сдвиговый регистр, а за строки второй. Следовательно, вывод строки будет состоять из двух последовательных записей в регистр: сначала передаем код строки, затем код точек в этой строке. В этой программе мы также воспользуемся ускоренной версией функции digitalWrite. Это необходимо для того, чтобы процесс динамической индикации проходил очень быстро. В противном случае, мы увидим заметное мерцание матрицы. Исходный код const byte data_pin = PD2; const byte st_pin = PD3; const byte sh_pin = PD4; unsigned long tm, next_flick; const unsigned int to_flick = 500; byte line = 0; const byte data = { 0b00111100, 0b01000010, 0b10100101, 0b10000001, 0b10100101, 0b10011001, 0b01000010, 0b00111100 }; void latchOn(){ digitalWriteFast(st_pin, HIGH); digitalWriteFast(st_pin, LOW); } void fill(byte d){ for(char i=0; i<8; i++){ digitalWriteFast(sh_pin, LOW); digitalWriteFast(data_pin, d & (1< next_flick){ next_flick = tm + to_flick; line++; if(line == 8) line = 0; // передаем код строки fill(~(1<<(7-line))); // зажигаем точки в строке № line fill(data); // открываем защелку latchOn(); } } Основная часть этой программы, включая переменные data_pin, sh_pin, st_pin, next_flick, to_flick и функцию fill уже известны нам из уроков про и про . Массив data хранит восемь строк нашей картинки. Для экономии памяти мы записали каждую комбинацию точек в бинарном виде. Функция latchOn открывает защелку регистра. Это нужно делать только после заполнения обоих сдвиговых регистров. После загрузки программы на Ардуино, на индикаторе появится смайл.

4. Анимация на светодиодной матрице

А теперь доработаем программу таким образом, чтобы изображение на индикаторе менялось каждые пол секунды. Для этого вспомним еще раз . const byte data_pin = PD2; const byte st_pin = PD3; const byte sh_pin = PD4; unsigned long tm, next_flick, next_switch; const unsigned int to_flick = 500; const unsigned long to_switch = 500000; byte line = 0; byte frame = 0; const byte data = { { 0b00111100, 0b01000010, 0b10100101, 0b10000001, 0b10100101, 0b10011001, 0b01000010, 0b00111100 }, { 0b00111100, 0b01000010, 0b10100101, 0b10000001, 0b10000001, 0b10111101, 0b01000010, 0b00111100 }}; void latchOn(){ digitalWriteFast(st_pin, HIGH); digitalWriteFast(st_pin, LOW); } void fill(byte d){ for(char i=0; i<8; i++){ digitalWriteFast(sh_pin, LOW); digitalWriteFast(data_pin, d & (1< next_flick){ next_flick = tm + to_flick; line++; if(line == 8) line = 0; fill(~(1<<(7-line))); fill(data); latchOn(); } tm = micros(); if(tm > next_switch){ next_switch = tm + to_switch; frame = !frame; } } Загружаем программу на Ардуино и наблюдаем результат.

5. Масштабирование светодиодной матрицы

Светодиодная матрица с разрешением 8×8 подойдет для отображения двух цифр или простого символа. Если требуется вывести на индикатор какое-то более или менее полезное изображение, необходимо объединить матрицы. Делается это с помощью добавления новых сдвиговых регистров как по вертикали, так и по горизонтали. Следует отметить, что быстродействия контроллера Ардуино Уно в связке со сдвиговыми регистрами хватит разве что на дисплей 16×16. Дальнейшее увеличение размеров светодиодного дисплея приведет к появлению заметного мерцания.

Задания

  • Гипноз. Запрограммировать контроллер таким образом, чтобы на светодиодной матрице с периодом в 1 секунду появлялись концентрические окружности с постоянно увеличивающимся радиусом.
  • Игра змейка. Реализовать на светодиодной матрице 8×8 такую известную игру, как змейка. В схему необходимо добавить четыре кнопки для управления направлением движения, а также зуммер для сигнализации события съедания яблок (или что там ест змея…).
  • Электронный уровень. Добавить в схему акселерометр. Написать программу, которая будет отображать на светодиодной матрице точку, координаты которой зависят от наклона всего устройства. Например, когда устройство зафиксировано параллельно земле (перпендикулярно вектору гравитации), то точка находится в центре. При наклоне электронного уровня влево, точка пропорционально смещается право.

Заключение

Собрав схему управления матрицей, у многих может возникнуть вопрос: «Ну неужели за 30 лет никто не придумал более простого способа работы с матрицей?» На самом деле, придумали. Существуют специализированные микросхемы для работы с разными типами дисплеев, в том числе и для работы со светодиодной матрицей. В одном из следующих уроков мы научимся управлять индикатором с помощью микросхемы MAX7219. Такой способ позволит нам легко объединять несколько матриц с один большой дисплей, без необходимости сильно усложнять электрическую схему.

Матричный дисплей - устройство состоящее из 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 по шкале Цельсия.

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

Включайся в дискуссию
Читайте также
Информационный портал по безопасности
Noize MC:
Ёлка - Вот это, да! (NEW). Кто сказал, что ёлка и кошка - это несовместимо? Елка кто сказал