Ардуино: управление светодиодной матрицей 8×8

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

Список необходимых компонентов

Для выполнения всех экспериментов в данном уроке, кроме самой светодиодной матрицы, потребуются: Ардуино-совместимый контроллер, сдвиговый регистр, макетная плата и немного проводов вилка-вилка. Необходимые компоненты можно добавить в корзину прямо здесь, и затем оформить заказ в нашем интернет-магазине.

В корзину
В корзину
В корзину
В корзину
В корзину
В корзину

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

Как мы уже знаем, сегментные индикаторы, будь то шкалы или цифры, состоят из отдельных светодиодов, соединенных вместе. Например, у группы светодиодов могут быть соединены все катоды. Такой индикатор имеет приписку «с общим катодом», в противном случае — «с общим анодом».

А что будет, если мы разместим светодиоды не в виде цифры или шкалы, а в виде сетки? Получится уже вполне себе графический индикатор. То есть такой, на котором можно отобразить не только число, но и какое-то изображение.

Такая сетка называется матричным индикатором, а в случае использования светодиодов —  светодиодной матрицей. Разрешение матричного индикатора — это количество точек по горизонтали и вертикали. Например, самые распространенные индикаторы имеют разрешение 8×8 точек.

Светодиодная матрица на Ардуино

Если требуется светодиодная матрица с большим разрешением, то её просто-напросто составляют из нескольких 8×8 индикаторов. Как это делать, мы увидим позже. А пока разберемся как соединяются все 64 светодиода внутри матрицы.

Конечно, можно бы было как и в случае семисегментного индикатора соединить все светодиоды общим катодом или анодом. В этом случае нам бы потребовалось либо 64 вывода контроллера, либо 8 сдвиговых регистров. Оба варианта весьма расточительны.

Более правильный вариант — объединить светодиоды в группы по 8 штук с общим катодом. Пусть это будут столбцы матрицы. Затем, параллельные светодиоды в этих столбцах объединить снова в группы по 8 штук уже с общим анодом. Получится вот такая схема:

Светодиодная матрица на Ардуино, схема

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

Светодиодная матрица на Ардуино, схема

Не выключая эту точку, попробуем зажечь другую — R3C7. Положительный контакт питания соединим с R3 и землю с C7. Но в таком случае строки R6 и R3 будут пересекаться с колонками C3 и C7 не в двух, а  в четырех местах! Следовательно и зажжется не две, а четыре точки. Проблема!

Светодиодная матрица на Ардуино, схема

Очевидно, что помочь сможет всё та же динамическая индикация. Если мы будем включать точки R6C3 и R3C7 по-очереди очень быстро, то сможем использовать персистентность зрения — способность интерпретировать быстро сменяющиеся изображения как одно целое.

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

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

Распиновка светодиодной матрицы 8x8

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

Принципиальная схема

Принципиальная схема светодиодная матрица Ардуино

Важное замечание №1. Необходимо, чтобы резисторы в этой схеме были на линиях, идущих от первого сдвигового регистра. Этот сдвиговый регистр отвечает за колонки. При таком подключении, каждый резистор будет задавать ток только для одного светодиода на каждом шаге динамического алгоритма. Следовательно, все светодиоды будут светиться равномерно.

Важное замечание №2. Указанная выше схема носит сугубо ознакомительный характер. Правильнее будет включить в разрыв между вторым регистром и матрицей дополнительную силовую микросхему, например транзисторную сборку ULN2003.

Программа

Чтобы было веселей, попробуем высветить на индикаторе смайлик. Как уже было сказано, для вывода изображения на матрицу воспользуемся динамической индикацией. А именно, будем высвечивать нашу картинку построчно. Сначала зажжем нужные колонки в самой верхней строке, затем во второй, в третьей, и так все 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[8] = {
    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<<i));
        digitalWriteFast(sh_pin, HIGH);
    }
}

void setPinFast(byte pin){
    DDRD |= _BV(pin);
}

void digitalWriteFast(byte pin, byte sig){
    if( sig )
        PORTD |= _BV(pin);
    else
        PORTD &= ~_BV(pin);
}

void setup() {
    setPinFast(data_pin);
    setPinFast(st_pin);
    setPinFast(sh_pin);
}

void loop() {
    tm = micros();
    if( tm > next_flick ){
        next_flick = tm + to_flick;
        line++;
        if( line == 8 )
            line = 0;
        // передаем код строки
        fill( ~(1<<(7-line)) );
        // зажигаем точки в строке № line
        fill( data[line] );
        // открываем защелку
        latchOn();
    }
}

Основная часть этой программы, включая переменные data_pin, sh_pin, st_pin, next_flick, to_flick и функцию fill уже известны нам из уроков про сдвиговый регистр и про динамическую индикацию.

Массив data хранит восемь строк нашей картинки. Для экономии памяти мы записали каждую комбинацию точек в бинарном виде.

Функция latchOn открывает защелку регистра. Это нужно делать только после заполнения обоих сдвиговых регистров.

После загрузки программы на Ардуино, на индикаторе появится смайл.

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

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

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[2][8] = {
{ 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<<i));
        digitalWriteFast(sh_pin, HIGH);
    }
}

void setPinFast(byte pin){
    DDRD |= _BV(pin);
}

void digitalWriteFast(byte pin, byte sig){
    if( sig )
        PORTD |= _BV(pin);
    else
        PORTD &= ~_BV(pin);
}

void setup() {
    setPinFast(data_pin);
    setPinFast(st_pin);
    setPinFast(sh_pin);

}

void loop() {
    tm = micros();
    if( tm > next_flick ){
        next_flick = tm + to_flick;
        line++;
        if( line == 8 )
            line = 0;
        fill( ~(1<<(7-line)) );
        fill( data[frame][7-line] );
        latchOn();
    }
 
    tm = micros();
    if( tm > next_switch ){
        next_switch = tm + to_switch;
        frame = !frame;
    }
}

Загружаем программу на Ардуино и наблюдаем результат.

Светодиодная матрица 8x8 на Ардуино

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

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

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

Задания

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

К размышлению

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

1. У нас в RobotClass как раз есть такой модуль с I2C интерфейсом. Легко подключается к Ардуино. Познакомиться с этим модулем можно на уроке «Модуль матрицы 8×8 с I2C интерфейсом от ROC«.

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


Изменено:

Ардуино: управление светодиодной матрицей 8×8: 14 комментариев

  1. А почему Вы во второй программе пишите:
    fill( data[frame][7-line] );
    ?
    Ведь в первой программе было:
    fill( data[line] );
    То есть изображение будет перевёрнутым.

  2. 1) Можно примерно в 2 раза увеличить быстродействие дисплея. Нет необходимости для каждой строки передавать в регистр все 8 бит кода строки, можно передать всего один бит и сразу его защелкнуть. Поэтому эти сдвиговые регистры лучше разъединить. А еще лучше для строк использовать регистр 74HC164 без защелки:
    http://easyelectronics.ru/sdvigovyj-registr.html

    2) В setup() желательно установить начальные значения next_flick и next_switch, а также поменять условия проверки на
    if( tm >= next_flick )
    и
    if( tm >= next_switch )
    Это также относится к уроку «Динамическая индикация»:
    http://robotclass.ru/tutorials/arduino-dynamic-led-indication/

  3. Ваши уроки самые лучшие из всех, которые я видел в интернете. Именно благодаря им я стал внимательно разбираться.

  4. Первый скетч не компилируется — выдает следующию ошибку
    /var/folders/qj/pm02y_4n44qbtwzxzmzs136h0000gn/T/arduino_modified_sketch_137520/sketch_nov21b.ino: In function ‘void setup()’:
    sketch_nov21b:49:16: error: ‘oe_pin’ was not declared in this scope
    setPinFast(oe_pin);
    ^~~~~~
    /var/folders/qj/pm02y_4n44qbtwzxzmzs136h0000gn/T/arduino_modified_sketch_137520/sketch_nov21b.ino:49:16: note: suggested alternative: ‘sh_pin’
    setPinFast(oe_pin);
    ^~~~~~
    sh_pin
    exit status 1
    ‘oe_pin’ was not declared in this scope

    • В программе нет ни одной инструкции #include, так что никакие дополнительные библиотеки не используются.

        • Эта функция описана в самой программе:
          void digitalWriteFast(byte pin, byte sig){
          if( sig )
          PORTD |= _BV(pin);
          else
          PORTD &= ~_BV(pin);
          }

          Проверьте, правильно ли вы написали её название.

  5. Здравствуйте.
    Как подключить матрицу?
    Подскажите, пожалуйста, кто знает.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.