Данная статья не претендует на толкование как единственно возможного метода отображения и прокрутки информации на светодиодной матрице. Я с удовольствием выслушаю ваши замечания и предложения по улучшению метода/функций. Данная статья – изложение личного опыта и описание результата, которого я добился.
В данной светодиодной матрице находится 64 красных светодиода. Если выводить контакты каждого светодиода отдельно, то понадобится 64 контакта на корпусе матрицы и микроконтроллер с 65 цифровыми портами ввода/вывода. Это нецелесообразно и невыгодно. Поэтому на заводе-изготовителе светодиоды объединяют в матрицы различных размеров (в нашем случае 8х8), то есть в 8 групп по строкам и столбцам следующим образом:
В таком случае нам понадобится лишь 16 цифровых вводов/выводов. Например, чтобы зажечь светодиод в левом верхнем углу, нам нужно на pin13 (см. картинку) подать лог 1, а на pin9 лог 0. Такой способ отображения называется статическая индикация.
«Хорошо, а если нам, например, нужно зажечь несколько светодиодов в разных позициях матрицы, а все остальные оставить выключенными?» — спросите Вы. При статической индикации это не представляется возможным. Для этого нужно использовать динамическую индикацию.
Быстро мелькающий объект кажется человеческому глазу постоянно светящимся. Это свойство человеческого глаза – инертность. Как вы догадались, именно на этом свойстве и основан метод вывода информации в светодиодной матрице. Например, чтобы вывести на «экран» некий символ, нужно последовательно, проходя все «пиксели» матрицы с высокой скоростью, включать светодиод в нужном месте.
Условия, которые необходимо соблюдать при программировании матриц динамической индикации:
1. Длительность отображения каждого столбца/строки («пикселя» в моем случае) постоянна, одинакова для всех столбцов/строк («пикселей» в моем случае).
2. Частота смены столбцов/строк («пикселей» в моем случае) не меняется.
Признаюсь, когда у меня появилась отладочная плата на МК Atmega1280 и светодиодная матрица, я и понятия не имел что такое динамическая индикация и для чего она нужна. До понимания необходимости ее использования я дошел методом проб и методом «тыка».
Принцип действия прошивки:
1. Вывод символа на экран
Давайте представим, что наша светодиодная матрица – это двухмерный массив размерностью I на J, совпадающей с размерностью нашей матрицы. В нашем случае это 8 на 8 пикселей. Итак, есть двухмерный массив типа boolean. К примеру:
В цикле мы проверяем, если элемент массива A[i][j]=1, тогда включаем светодиод на матрице, находящийся в позиции (I;J), делаем паузу на отображения светодиода и выключаем светодиод в позиции (I;J). В результате работы программы по такому алгоритму на экране матрицы выведется символ «А» (в двумерном массиве именно этот символ отображен). Назовем этот алгоритм «Вывод на экран». Итак, с выводом информации на экран с помощью динамической индикации разобрались.
2. Прокрутка информации на экране
Конечно, классно выводить на экран неподвижную информацию, но было бы интереснее «оживить» ее – заставить передвигаться. Рассмотрим алгоритм перемещения информации:
В бесконечном цикле выполняем:
1. Вызываем алгоритм «Вывод на экран».
2. Берем 1й столбец матрицы А и записываем его в буфер (двухмерный массив BUFFER[8][1]).
3. Записываем содержимое матрицы с позиции A[i][j+1] в позицию A[i][j]. То есть мы по сути «сдвинули» матрицу влево на один столбец.
4. Записываем в последний столбец матрицы А (в свободный, так как мы сдвинули матрицу влево на один столбец) содержание буфера BUFFER. Назовем этот алгоритм «Прокрутка информации».
Вот и весь алгоритм прокрутки информации на экране. Теперь, зная алгоритмы вывода и прокрутки информации с помощью динамической индикации, мы можем приступить к практике.
Каждый из 16ти выводов матрицы пронумерован. В соответствии этим номерам были подпаяны и выведены 8 контактов, отвечающих за строки, и 8 контактов, отвечающих за столбцы. У меня, например, матрица подключена так:
Выводы, отвечающие за строки 23,25,27,29,31,33,35,37 – это номера выводов ножек микроконтроллера;
Выводы, отвечающие за столбцы 39,41,43,45,47,49,51,53 — это номера выводов ножек микроконтроллера.
Программирование и прошивка микроконтроллера производится в среде разработки Arduino IDE.
Что необходимо знать для понимания кода программы, написанного ниже:
Функция pinMode()
Устанавливает режим работы заданного вход/выхода(pin) как входа или как выхода.
pinMode(pin, mode), где pin: номер вход/выхода(pin), который Вы хотите установить; mode: режим одно из двух значение — INPUT или OUTPUT, устанавливает на вход или выход соответственно.
Функция digitalWrite()
Подает HIGH или LOW значение на цифровой вход/выход (pin).
digitalWrite(pin, value), где pin: номер вход/выхода(pin); value: значение HIGH или LOW
Функция delayMicroseconds()
Останавливает выполнение программы на заданное в параметре количество микросекунд (1 000 000 микросекунд в 1 секунде).
delayMicroseconds(us), где us: количество микросекунд, на которое приостанавливается выполнение программы. (unsigned int)
Структура любой программы (скетча, так как в среде разработки ардуино программы называются именно так) имеет следующий вид:
В функции setup() производятся настройки портов ввода/вывода МК, настройка подключенных устройств к МК, периферии МК, а так же выполняется все то, что нужно выполнить ОДИН раз.
В функции loop() пишется тело программы, которое будет выполняться циклически, пока микроконтроллер включен.
Выполним настройку выводов микроконтроллера. Состояний портов ввода/вывода существует два вида: настроен на вход либо на выход. В нашем случае нужно настроить на выход.
Давайте теперь реализуем алгоритмы, которые я описал выше. Итак, мы имеем двухмерный массив:
Напишем функцию, которая реализует алгоритм «Вывод на экран».
Напишем функцию, которая реализует алгоритм «Прокрутка информации».
Теперь приведу полный код программы:
Вот и все. Заливайте эту программу в свой микроконтроллер и наблюдайте, как буква «А» будет «бежать» влево. Добавлю, что массивом размерностью 8 на 8 элементов ограничиваться не стоит. Например, я использовал массив размером 8 на 86. Написал в нем целую фразу, как видно на этом видео.
Удачи!
Немного теории
В данной светодиодной матрице находится 64 красных светодиода. Если выводить контакты каждого светодиода отдельно, то понадобится 64 контакта на корпусе матрицы и микроконтроллер с 65 цифровыми портами ввода/вывода. Это нецелесообразно и невыгодно. Поэтому на заводе-изготовителе светодиоды объединяют в матрицы различных размеров (в нашем случае 8х8), то есть в 8 групп по строкам и столбцам следующим образом:
В таком случае нам понадобится лишь 16 цифровых вводов/выводов. Например, чтобы зажечь светодиод в левом верхнем углу, нам нужно на pin13 (см. картинку) подать лог 1, а на pin9 лог 0. Такой способ отображения называется статическая индикация.
«Хорошо, а если нам, например, нужно зажечь несколько светодиодов в разных позициях матрицы, а все остальные оставить выключенными?» — спросите Вы. При статической индикации это не представляется возможным. Для этого нужно использовать динамическую индикацию.
Динамическая индикация
Быстро мелькающий объект кажется человеческому глазу постоянно светящимся. Это свойство человеческого глаза – инертность. Как вы догадались, именно на этом свойстве и основан метод вывода информации в светодиодной матрице. Например, чтобы вывести на «экран» некий символ, нужно последовательно, проходя все «пиксели» матрицы с высокой скоростью, включать светодиод в нужном месте.
Условия, которые необходимо соблюдать при программировании матриц динамической индикации:
1. Длительность отображения каждого столбца/строки («пикселя» в моем случае) постоянна, одинакова для всех столбцов/строк («пикселей» в моем случае).
2. Частота смены столбцов/строк («пикселей» в моем случае) не меняется.
Принцип действия прошивки микроконтроллера
Признаюсь, когда у меня появилась отладочная плата на МК Atmega1280 и светодиодная матрица, я и понятия не имел что такое динамическая индикация и для чего она нужна. До понимания необходимости ее использования я дошел методом проб и методом «тыка».
Принцип действия прошивки:
1. Вывод символа на экран
Давайте представим, что наша светодиодная матрица – это двухмерный массив размерностью I на J, совпадающей с размерностью нашей матрицы. В нашем случае это 8 на 8 пикселей. Итак, есть двухмерный массив типа boolean. К примеру:
boolean A[8][8] =
{0,0,1,1,1,1,0,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,1,1,1,1,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0}
В цикле мы проверяем, если элемент массива A[i][j]=1, тогда включаем светодиод на матрице, находящийся в позиции (I;J), делаем паузу на отображения светодиода и выключаем светодиод в позиции (I;J). В результате работы программы по такому алгоритму на экране матрицы выведется символ «А» (в двумерном массиве именно этот символ отображен). Назовем этот алгоритм «Вывод на экран». Итак, с выводом информации на экран с помощью динамической индикации разобрались.
2. Прокрутка информации на экране
Конечно, классно выводить на экран неподвижную информацию, но было бы интереснее «оживить» ее – заставить передвигаться. Рассмотрим алгоритм перемещения информации:
В бесконечном цикле выполняем:
1. Вызываем алгоритм «Вывод на экран».
2. Берем 1й столбец матрицы А и записываем его в буфер (двухмерный массив BUFFER[8][1]).
3. Записываем содержимое матрицы с позиции A[i][j+1] в позицию A[i][j]. То есть мы по сути «сдвинули» матрицу влево на один столбец.
4. Записываем в последний столбец матрицы А (в свободный, так как мы сдвинули матрицу влево на один столбец) содержание буфера BUFFER. Назовем этот алгоритм «Прокрутка информации».
Вот и весь алгоритм прокрутки информации на экране. Теперь, зная алгоритмы вывода и прокрутки информации с помощью динамической индикации, мы можем приступить к практике.
Подключение матрицы к отладочной плате
Каждый из 16ти выводов матрицы пронумерован. В соответствии этим номерам были подпаяны и выведены 8 контактов, отвечающих за строки, и 8 контактов, отвечающих за столбцы. У меня, например, матрица подключена так:
Выводы, отвечающие за строки 23,25,27,29,31,33,35,37 – это номера выводов ножек микроконтроллера;
Выводы, отвечающие за столбцы 39,41,43,45,47,49,51,53 — это номера выводов ножек микроконтроллера.
Программирование
Программирование и прошивка микроконтроллера производится в среде разработки Arduino IDE.
Что необходимо знать для понимания кода программы, написанного ниже:
Функция pinMode()
Устанавливает режим работы заданного вход/выхода(pin) как входа или как выхода.
pinMode(pin, mode), где pin: номер вход/выхода(pin), который Вы хотите установить; mode: режим одно из двух значение — INPUT или OUTPUT, устанавливает на вход или выход соответственно.
Функция digitalWrite()
Подает HIGH или LOW значение на цифровой вход/выход (pin).
digitalWrite(pin, value), где pin: номер вход/выхода(pin); value: значение HIGH или LOW
Функция delayMicroseconds()
Останавливает выполнение программы на заданное в параметре количество микросекунд (1 000 000 микросекунд в 1 секунде).
delayMicroseconds(us), где us: количество микросекунд, на которое приостанавливается выполнение программы. (unsigned int)
Структура любой программы (скетча, так как в среде разработки ардуино программы называются именно так) имеет следующий вид:
void setup()
{
…
}
void loop()
{
…
}
В функции setup() производятся настройки портов ввода/вывода МК, настройка подключенных устройств к МК, периферии МК, а так же выполняется все то, что нужно выполнить ОДИН раз.
В функции loop() пишется тело программы, которое будет выполняться циклически, пока микроконтроллер включен.
Выполним настройку выводов микроконтроллера. Состояний портов ввода/вывода существует два вида: настроен на вход либо на выход. В нашем случае нужно настроить на выход.
const int row[8]={23,25,27,29,31,33,35,37}; // Помните 8 выводов, отвечающих за строки и
const int col[8]={39,41,43,45,47,49,51,53}; // столбцы? А вот и они.
void setup()
{
for (int i=0; i<8; i++)
{
pinMode(row[i],OUTPUT); // В цикле все выводы переводим в режим «на вывод»
pinMode(col[i],OUTPUT); // Выключаем все светодиоды
}
for (int i=0; i<8; i++)
{
digitalWrite(row[i],HIGH);
}
}
Давайте теперь реализуем алгоритмы, которые я описал выше. Итак, мы имеем двухмерный массив:
boolean A[8][8] =
{0,0,1,1,1,1,0,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,1,1,1,1,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0}
Напишем функцию, которая реализует алгоритм «Вывод на экран».
int v=3; //Скорость прокручивания информации. То есть по сути это количество итераций цикла отрисовки информации в матрице
int dms=400; //Скорость обновления «пикселей». То есть время, когда светодиод находится во включенном состоянии. Чем оно больше, тем более заметнее глазу будет мерцание изображения
void paint(boolean screen[8][8], int v) //В качестве параметров передаем матрицу и параметр v
{
int i, j;
for (int c=0; c <v; c ++) // Тот самый цикл задержки отрисовки информации
{
for (i=0; i<8; i++)
for (j=0; j<8; j++)
{
if (screen[i][j]= =1) // Если элемент массива = 1
{
digitalWrite(row[i], LOW);
digitalWrite(col[j], HIGH);
delayMicroseconds(dms);
digitalWrite(row[i], HIGH);
digitalWrite(col[j], LOW); // То включить светодиод в проверяемой позиции, задержать включенным, выключить светодиод.
}
else
{
digitalWrite(row[i], HIGH);
digitalWrite(col[j], LOW);
delayMicroseconds(dms);
digitalWrite(row[i], HIGH);
digitalWrite(col[j], LOW);
} // Иначе выключить светодиод в проверяемой позиции, задержать включенным, обновить экран. (даже если светодиод и был выключенным в этой позиции, то все равно его нужно «выключить» для того, чтобы проверка в цикле и действия проходили с одинаковой задержкой. Если в позиции 1 – то выполняются действия, если в позиции 0 – все равно должно что-то выполнится либо выполнится задержка, сопоставимая с задержкой выполнения включения светодиода)
}
}
}
Напишем функцию, которая реализует алгоритм «Прокрутка информации».
void scroll(boolean screen[8][8]) // В качестве параметра передаем наш двухмерный массив
{
boolean buf[8][1];
for (int i=0; i<8; i++)
{
buf[i][0]=screen[i][0]; // Считали 1ю колонку в буфер
}
for (int i=0; i<8; i++)
for (int j=0; j<8; j++)
{
screen[i][j]=screen[i][j+1]; // Сдвинули матрицу на один столбец влево
}
for (int i=0; i<8; i++)
{
screen[i][8-1]=buf[i][0]; // Записали содержимое буфера (первую колонку) в конец матрицы
}
}
Теперь приведу полный код программы:
const int row[8]={23,25,27,29,31,33,35,37};
const int col[8]={39,41,43,45,47,49,51,53};
int v=3;
int dms=400;
boolean A[8][8] =
{0,0,1,1,1,1,0,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,1,1,1,1,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0,
0,1,0,0,0,0,1,0};
void setup()
{
for (int i=0; i<8; i++)
{
pinMode(row[i],OUTPUT);
pinMode(col[i],OUTPUT);
}
for (int i=0; i<8; i++)
{
digitalWrite(row[i],HIGH);
}
}
void loop()
{
paint(A, v);
scroll(A);
}
void paint(boolean screen[8][8], int v)
{
int i, j;
for (int c=0; c <v; c ++)
{
for (i=0; i<8; i++)
for (j=0; j<8; j++)
{
if (screen[i][j]==1)
{
digitalWrite(row[i], LOW);
digitalWrite(col[j], HIGH);
delayMicroseconds(dms);
digitalWrite(row[i], HIGH);
digitalWrite(col[j], LOW);
}
else
{
digitalWrite(row[i], HIGH);
digitalWrite(col[j], LOW);
delayMicroseconds(dms);
digitalWrite(row[i], HIGH);
digitalWrite(col[j], LOW);
}
}
}
}
void scroll(boolean screen[8][8])
{
boolean buf[8][1];
for (int i=0; i<8; i++)
{
buf[i][0]=screen[i][0];
}
for (int i=0; i<8; i++)
for (int j=0; j<8; j++)
{
screen[i][j]=screen[i][j+1];
}
for (int i=0; i<8; i++)
{
screen[i][8-1]=buf[i][0];
}
}
Вот и все. Заливайте эту программу в свой микроконтроллер и наблюдайте, как буква «А» будет «бежать» влево. Добавлю, что массивом размерностью 8 на 8 элементов ограничиваться не стоит. Например, я использовал массив размером 8 на 86. Написал в нем целую фразу, как видно на этом видео.
Удачи!