LED матрица 8x8 и регистры 74HC164

Дата публикации: Mar 19, 2013 9:35:56 PM

Захотелось поиграть со светодиодами. Решил подключить LED матрицу 8х8 к Arduino, а заодно написать небольшую статью.

На основе LED матриц сделано очень много эффектных электронных проектов. Например, есть интересный тетрис на основе PIC контроллера. Или вот такой интерактивный конструктор. Имеются даже готовые модули для Arduino с разноцветными матрицами от adafruit и прочих производителей. 

Принцип действия

Устроена такая матрица достаточно просто:

Как видно на схеме, восемь выводов матрицы соединяются с анодами светодиодов, и еще восемь - с катодами. Таким образом, соединяя один из анодов (например, R4) с плюсом питания, а один из катодов (C5) - с минусом, получаем горящий светодиод на пересечении этих линий.

 

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

Сдвиговый регистр

Итак, зажечь один светодиод в матрице легче легкого, а как сделать так, чтобы этот светодиод двигался? Не намного сложнее, с программной точки зрения. Просто через каждые, например, 40 миллисекунд переключаем питание на другой катод и анод. В итоге получится иллюзия движения, анимация.

 

Но если попытаться собрать такое устройство в реальности, возникнут две большие проблемы. Во-первых, далеко не во всех микроконтроллерах есть 16 GPIO выходов. А если матрицы будет две? потребуется уже, как минимум, 24 цифровых выхода. Да и занимать все выходы контроллера только лишь для работы LED-матрицы - не очень правильное решение.

Во-вторых, во всех микроконтроллерах имеется некоторый порог суммарной мощности GPIO выходов. Так, у Arduino Uno, этот порог составляет 200мА. То есть можно одновременно зажечь только 20 светодиодов из 64. Если же в матрице стоят диоды мощностью более 40мА, то её вообще нельзя будет напрямую соединять с большинством микроконтроллеров. 

Решить проблемы можно с помощью добавления в схему специального устройства - сдвигового регистра. Этот электронный прибор представляет собой ни что иное, как FIFO буфер. Причем, нас интересует регистр с выходами со всех ячеек буфера. Что это значит? Это значит, что мы можем наполнить восемь ячеек буфера через всего один выход микроконтроллера, а затем слить данные со всех ячеек на матрицу.

Использование такого буфера, позволяет нам снизить нагрузку на микроконтроллер во всех смыслах. Во-первых, светодиоды теперь питает непосредственно регистр, а не контроллер. Во вторых, вместо 16 выходов, теперь потребуется подключить всего два: один для передачи данных, другой - для сигнала синхронизации.

Работать с регистром очень просто. Мы просто устанавливаем на входе данных регистра нужное значение, и затем пускаем импульс на вход синхронизации. Ниже представлен небольшой участок кода на Arduino, отвечающий за заполнение регистра данными.

Заполнение регистра

int data[] = {1,0,1,0,1,0,1,0};

for( int k=0; k<8; k++ ){

    digitalWrite( dPin, data[k] );

    digitalWrite( cpPin, HIGH );

    digitalWrite( cpPin, LOW );

}

Смайл на LED матрице

Имея регистры, мы можем легко управлять любыми анодами и катодами LED дисплея. Но вот беда, какие бы аноды и катоды мы не выбирали - зажечь на матрице смайл просто невозможно! Попробуйте придумать такую комбинацию для представленного ниже рисунка :)

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

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

Реализация

В качестве регистра я использовал 74HC164N. Прибор выполнен в варианте КМОП, что позволяет подключать его к источнику питания +3.3В. Но у данного регистра есть большой минус - у него нет замка. Это значит, что выходы ячеек буфера всегда будут подключены к выходам микросхемы. И когда мы заполняем регистр данными, они тут же появляются на выходе и вызывают тем самым, характерную засветку матрицы. Чтобы минимизировать засветку, мне даже пришлось ускорить заливку каждого кадра развертки с помощью функции digitalWriteFast (смотри код ниже).

Для сравнения, рассмотрим регистр 74HC595, у которого есть замок. Пока замок закрыт, на выходах микросхемы находится высокий импеданс. Такие выходы можно считать разомкнутыми, и матрица при этом полностью гасится. После того как все нужные данные переданы в буфер, замок открывается и на матрица зажигается рисунок. Благодаря такому механизму, заполнение регистров остается за кадром, и на матрице не появляется засветка.

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

Вот и программа, которая не просто рисует смайл, а меняет каждую секунду грустный смайл на веселый (который по сути уже и не смайл...).

LED-матрица + Arduino + 74HC164

const int aPin =  2;

const int bPin =  3;

const int cpPin = 4;

const unsigned int switch_TO = 1000;

unsigned int time;

unsigned int last = 0;

byte img = 0;

int p_data[][8] = {{

B00111100,

B01000010,

B10100101,

B10000001,

B10100101,

B10011001,

B01000010,

B00111100

},{

B00111100,

B01000010,

B10100101,

B10000001,

B10011001,

B10100101,

B01000010,

B00111100

}};

int a_mask[] = {2,1,14,12,10,13,4,15};

int b_mask[] = {8,11,5,3,7,6,9,0};

void setup() {

    pinMode(aPin, OUTPUT);      

    pinMode(bPin, OUTPUT);      

    pinMode(cpPin, OUTPUT);

    Serial.begin(9600);    

}

int get_level(int val){

    if( val )

        return HIGH;

    else

        return LOW;

}

void digitalWriteFast( int pin, int val ){

    if( val )

        PORTD |= 1<<pin;

    else

        PORTD &= ~(1<<pin);

}

void set_image(byte img){

    int a, b;

    long vector;

    for( int i=0; i<8; i++ ){

        vector = (1 << (i+8)) | ~p_data[img][i] & B11111111;

        for( int k=7; k>=0; k-- ){

            digitalWriteFast(aPin, vector & (1<<a_mask[k]));

            digitalWriteFast(bPin, vector & (1<<b_mask[k]));

            digitalWriteFast(cpPin, HIGH);

            digitalWriteFast(cpPin, LOW);

        }

        delay(2);

    }

}

void loop(){

    time = millis();

    if( time > last + switch_TO ){

        img = img==1 ? 0 : 1;

        last = time;

    }

    set_image( img );

}

Как я не люблю эти макетные платы под пайку, такой грязный монтаж...

        

И, наконец, видеоролик.

Спешу заметить, что я отлично отражал недостатки микросхемы 74HC164 при создании данного устройства. Но для меня было важно оценить эффект отсутствия замка, что, я считаю, вполне получилось. Следующий этап - изготовление модуля для Arduino/RaspberryPi на базе правильных регистров 74HC595.