Разбираюсь с низкоуровневым программированием матрицы. Это жесть!
Логика работы такая.
Матрица фактически разделена на два блока, верхние 16 строк и нижние 16 строк. В первые пишут через R1, G1 и B1, во вторые, соответственно, через R2, G2 и B2.
Одновременно светится только одна строка. Для полноценного свечения нужно просто организовывать развёртку. Фактически памяти там в каждой половине экрана — один сдвиговый регистр + выбор адреса, куда ему светить.
Сигналом LAT (пиком высокого уровня) сигнализируем, чтобы сдвиговый регистр принял строку данных. Дальше в цикле 64 раза выставляем данные (R1,G1,B1,R2,G2,B2) и запихиваем их низким импульсом CLK. Сразу в оба сдвиговых регистра.
Входами A, B, C, D выбираем строку, в которой светится этот сдвиговый регистр. Точнее, будут светиться две строки, в первой и второй половинах.
Для того, чтобы избегать мерцания и призраков, высоким уровнем OE на время манипуляций можно гасить отображение.
Т.е. полный цикл выглядит примерно так:
code cpp
// Цикл по строкам в каждой половине экрана
for(int row=0; row<16; row++)
{
// Вырубаем отображение
digitalWrite(OE, HIGH);
// Указываем принять данные
digitalWrite(LAT, HIGH);
digitalWrite(LAT, LOW);
// Цикл вывода светодиодов в строке
for(int i=0; i<64; i++)
{
// Выставляем биты включения светодиодов
digitalWrite(R1, r1);
digitalWrite(G1, g1);
digitalWrite(B1, b1);
digitalWrite(R2, r2);
digitalWrite(G2, g2);
digitalWrite(B2, b2);
// Отправляем выставленное значение
digitalWrite(CLK, LOW);
digitalWrite(CLK, HIGH);
}
// К этому месту строка светодиодов загружена
// Выставляем адрес для отображения
digitalWrite(A, row & B00000001);
digitalWrite(B, row & B00000010);
digitalWrite(C, row & B00000100);
digitalWrite(D, row & B00001000);
digitalWrite(OE, LOW);
}
Правда, в таком виде цикл вывода будет работать ОЧЕНЬ медленно (digitalWrite работает медленно!), экран будет очень тёмный и будет мерцать, т.к. заполнение матрицы будет происходить бОльшую часть времени.
Для ускорения работы нужно делать запись сразу всего блока цветов одной записью в регистр. Например, в случае MEGA вешаем R1,G1,B1,R2,G2,B2 на, соответственно, 24,25,26,27,28 и 29 пин. И тогда это будут биты со 2 по 7 регистра PORTA. А CLK пусть висит на 11 порту, это 5 бит PORTB. Внутренний цикл станет примерно таким:
code cpp
// Цикл вывода светодиодов в строке
for(int i=0; i<64; i++)
{
// Выставляем биты включения светодиодов (младшие два бита обнуляются)
PORTA = (r1<<2) | (g1 << 3) | (b1 << 4) | (r2 << 5) | (g2 << 6) | (b2 << 7);
// Отправляем выставленное значение
PORTB &= ~0x20; // pin 11, bit 5
PORTB |= 0x20; // pin 11, bit 5
}
Вот в таком виде оно будет выводиться очень быстро! Хотя яркость будет не максимальной, так как заметную часть времени экран будет погашен через OE. Можно после включения OE добавлять небольшую паузу, тогда мерцания будет чуть больше, но и яркость станет выше. Тут надо будет ещё поэкспериментировать
Например, если потребна максимальная яркость, можно вообще не использовать гашение по OE.
...
Конечно, всё это уже реализовано в готовой библиотеке, но под STM32 её надо радикально переписывать
Так что пришлось разобраться, как оно работает.