Организация бегущей строки на светодиодной матрице, плюс вопрос о типах данных.
- Войдите на сайт для отправки комментариев
Здравствуйте. Проект, описанный в соседней теме (Подкиньте идею по реализации дисплея из 9 светодиодных матриц.) в разгаре. Прежде чем браться за матрицу 24Х24, решил попрактиковаться на матрице 8Х8. В качестве "самого сложного" реализовал бегущую строку. Только вот "бегает" у меня только то, что не превышает размера матрицы, таков алгоритм. А как заставить "бежать" строку длинее длинны матрицы? Скетч:
//Пин подключен к ST_CP входу 74HC595
int latchPin = 8;
//Пин подключен к SH_CP входу 74HC595
int clockPin = 12;
//Пин подключен к DS входу 74HC595
int dataPin = 11;
//RST пин
#define rst1 5
//Количество пропущенных кадров, до следующего "шага" анимации
#define maxcount 50
//Количество строк матрицы
#define matrixROWS 8
//Количество столбцов матрицы
#define matrixCOLS 8
//Изначальное смещение строки - сдвинута за границу матрицы
int cols_offset=-matrixCOLS;
int counter=0;
uint8_t rowbyte;
uint8_t colbyte;
//Строка для отображения
uint8_t A[8] =
{B00000100,\
B00001100,\
B00011100,\
B00101100,\
B00001100,\
B00001100,\
B00001100,\
B00011110};
void setup() {
//устанавливаем режим OUTPUT
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(rst1, OUTPUT);
}
void loop() {
//Отображаем первую строку
rowbyte=B10000000;
//Показываем кадр
for (int i=0; i<=7; i++) {
digitalWrite(rst1, LOW);
digitalWrite(rst1, HIGH);
digitalWrite(latchPin, LOW);
//Rows
shiftOut(dataPin, clockPin, MSBFIRST, rowbyte>>i);
//Cols
//Определяем, в какую сторону сдвигаем строку
if (cols_offset < 0 ) {colbyte=A[i]>>(cols_offset*-1);}
if (cols_offset >= 0 ) {colbyte=A[i]<
Ещё волнующий меня вопрос: для длинных матриц развёртку лучше делать горизонтальную или вертикальную?
С бегущими строками закончили. Дальше просто о отображении на матрице:
Если я планирую отображать изменяющиеся данные (курс акций например :) ), то каким образом сформировать очередной кадр? Я думаю разделить каждую строку на зоны (вот зона для отображения первой цифры числа, вот для второй и т.д.), в зависимости от выводимых данных формировать число, описывающее все биты этой строки, ну и отдавать это число в регистры. Собственно, как можно "слеплять" строки? Пример: Есть числа 00111000, 00110000 и 00001100. Как получить из них 001110000011000000001100? И я правильно понимаю, что мне тогда нужно будет делать сначала shiftOut(dataPin, clock, MSBFIRST, (data >> 16)), затем data >> 8, и на последок просто data?
Э... Честно говоря, от меня ускользает смысл некоторых действий. Но идея, как я понял в том, что бы просто выводить только часть массива, зная текущее смещение. Правильно? До такого решения "в лоб" я не додмуался, почему то :(
А что насчёт второй части вопроса? Кажется, я выбрал не самую лучшую реализацию...
Идея такая - есть функция, которая выводит данные на дисплей, причем эта функция работает только с массивом размером таким же как и дисплей, что-то типа видео-памяти. А вы уже как хотите так и заполняйте эту память, то есть вам не нужно думать как вывести на дисплей, вам просто нужно правильно заполнить массив видеопамяти.
Насчёт вывода изменяющейся информации придумал - нужно определить область, гарантированно вмещающую "длинное" число (999), и завести нужного размера маску из нулей. А потом эту маску умножать на нужные биты, в зависимости от выводимого числа.
У меня к несчастью (или к счастью?) матрица управляется регистрами сдвига. Поэтому использовать ваш код напрямую не получится. Написал такой код:
//---------------------------------------- //Выводы сдвигивого регистра //Регистр для строк int COLSclockPin = 11; //Пин подключен к SH_CP #define COLSdataPin 9 //Пин подключен к DS #define COLSlatchPin 7 //Пин подключен к ST_CP //Регистр для столбцов int ROWSclockPin = 12; //Пин подключен к SH_CP #define ROWSdataPin 10 //Пин подключен к DS #define ROWSlatchPin 8//Пин подключен к ST_CP //RST пин общий #define rst 6 //----------------------------------------- //Описание матрицы #define COLS 8 //Физические столбцы #define ROWS 8 //Физические строки //----------------------------------------- //Описание анимации #define lenght 32 //Общая длинна строки для отображения (сколько столбцов) #define offset 1 //На сколько столбцов продвигать строку #define less_frame 100 //Сколько кадров пропустить перед след.шагом анимации (скорость анимации) //----------------------------------------- //Вспомогательные переменные: //frameposition фактически задаёт последний элемент массива, //который будет использован для вывода на матрицу //Первый элемент вычисляется, исходя из факт. кол-ва столбцов //То есть, если frameposition=16, а столбцов 8, то выводятся элементы с 9 по 16 int frameposition=COLS; //Сколько кадров прошло int now_frames=0; //"Болванка" для столбца. Конкретный зажигается смещением. byte nowrow=B00000001; //----------------------------------------- //Собственно, строка для вывода: uint8_t string[lenght] = {B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ //--------- B11111111,\ B11111110,\ B11111100,\ B11111000,\ B11110000,\ B11100000,\ B11000000,\ B10000000,\ //-------- B10000000,\ B11000000,\ B11100000,\ B11110000,\ B11111000,\ B11111100,\ B11111110,\ B11111111,\ //-------- B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000,\ B00000000}; void setup() { Serial.begin(9600); pinMode(COLSlatchPin, OUTPUT); pinMode(COLSclockPin, OUTPUT); pinMode(COLSdataPin, OUTPUT); pinMode(ROWSlatchPin, OUTPUT); pinMode(ROWSclockPin, OUTPUT); pinMode(ROWSdataPin, OUTPUT); pinMode(rst, OUTPUT); digitalWrite(rst, LOW); digitalWrite(rst, HIGH); } //Высчитывает номер элемента массива, //соответсвующего этому столбцу int elementnumber(int i) { return frameposition-(COLS-i); } void loop() { for (int i=0; i<COLS; i++) { digitalWrite(rst, LOW); digitalWrite(rst, HIGH); digitalWrite(COLSlatchPin, LOW); digitalWrite(ROWSlatchPin, LOW); //Подаём питание на столбцы shiftOut(COLSdataPin, COLSclockPin, MSBFIRST, nowcol<<i); //Зажигаем нужную строку: shiftOut(ROWSdataPin, ROWSclockPin, MSBFIRST, string[elementnumber(i)]); digitalWrite(ROWSlatchPin, HIGH); digitalWrite(COLSlatchPin, HIGH); delay(1); } //Пересчитываем необходимые счётчики: now_frames++; if (now_frames==less_frame) { now_frames=0; frameposition+=offset; if (frameposition>lenght) {frameposition=COLS;} } }Проблема в следующем:
когда матрица была подключена с помощью каскада из двух регистров, анимация работала отлично. Но сейчас решил разделить регистры. Один для столбцов, другой для строк.
После разделения поправил код и увидел нечто дикое вместо адекватной картинки. После разбирательств и правок удалось добиться чёткой картинки, но с "паразитной" засветкой соседних пикселей. Происходит это так, как будто при подаче питания на очередной столбец, питание со строк ещё не снято. Так и было. Но потом я стал "открывать" защёлку сразу обоих регистров, что сделало "паразитную" засветку предельно тусклой. Однако, если я меняю порядок "защёлкивания" регистров (сначала строки, потом столбцы, и наоборот), яркость "лишних" пикселей заметно меняется. Наиболее тускло они светятся, если сначала защёлкивать строки. Что ещё странно, увеличение задержки добавляет мерцания, но не убирает паразитного свечения.
Как уже догадался пытливый читатель, с использованием каскада, когда регистры предельно синхронны, таких проблем нет.
Есть идеи?
Я так подозреваю, что можно обойтись и без ресета.
Виной засветкам являются монтажные ( и другие) емкости.
Попробуйте алгоритм (мультипликация построчная)
Паузой поиграться, возможно, и не потребуется.
Смысл в том, что строки должны зажигаться позже, а гаситься раньше столбцов.
Обратите внимание, что гасить и зажигать нужно не защелкой, а выбором (CS), т.е. в погашенном случае выходы регистра должны быть отключены.
Я так подозреваю, что можно обойтись и без ресета.
Виной засветкам являются монтажные ( и другие) емкости.
Попробуйте алгоритм (мультипликация построчная)
Паузой поиграться, возможно, и не потребуется.
Смысл в том, что строки должны зажигаться позже, а гаситься раньше столбцов.
Обратите внимание, что гасить и зажигать нужно не защелкой, а выбором (CS), т.е. в погашенном случае выходы регистра должны быть отключены.
Непременно опробую ваш совет, но если проблема в ёмкостях, то и при использовании каскада должна быть засветка.
UPD.
"CS" - это всмысле OE (pin 13) "Вход для переключения состояния выходов из высокоомного в рабочее"?
UPD2
По вашему алгоритму получается ооочень тускло. Однако, паразитная засветка исчезла сама собой. Я в замещательстве. Порядок передачи байта в регистры я менял много раз, сброс переносил в разные места и убирал... Непонятно.
Я так подозреваю, что можно обойтись и без ресета.
Виной засветкам являются монтажные ( и другие) емкости.
Попробуйте алгоритм (мультипликация построчная)
Паузой поиграться, возможно, и не потребуется.
Смысл в том, что строки должны зажигаться позже, а гаситься раньше столбцов.
Обратите внимание, что гасить и зажигать нужно не защелкой, а выбором (CS), т.е. в погашенном случае выходы регистра должны быть отключены.
Непременно опробую ваш совет, но если проблема в ёмкостях, то и при использовании каскада должна быть засветка.
UPD.
"CS" - это всмысле OE (pin 13) "Вход для переключения состояния выходов из высокоомного в рабочее"?
UPD2
По вашему алгоритму получается ооочень тускло. Однако, паразитная засветка исчезла сама собой. Я в замещательстве. Порядок передачи байта в регистры я менял много раз, сброс переносил в разные места и убирал... Непонятно.
В итоге, работает такой код:
//Индикация динамическая. Развёртка по столбцам //---------------------------------------- //Выводы сдвигивого регистра //Регистр для строк int COLSclockPin = 11; //Пин подключен к SH_CP #define COLSdataPin 9 //Пин подключен к DS #define COLSlatchPin 7 //Пин подключен к ST_CP #define COLSpoffpin 5 //Регистр для столбцов int ROWSclockPin = 12; //Пин подключен к SH_CP #define ROWSdataPin 10 //Пин подключен к DS #define ROWSlatchPin 8//Пин подключен к ST_CP #define ROWSpoffpin 4 //RST пин общий #define rst 6 //----------------------------------------- //Описание матрицы #define COLS 8 //Физические столбцы #define ROWS 8 //Физические строки //----------------------------------------- //Описание анимации #define lenght 32 //Общая длинна строки для отображения (сколько столбцов) #define offset 1 //На сколько столбцов продвигать строку #define less_frame 80 //Сколько кадров пропустить перед след.шагом анимации (скорость анимации) //----------------------------------------- //Вспомогательные переменные: //frameposition фактически задаёт последний элемент массива, //который будет использован для вывода на матрицу //Первый элемент вычисляется, исходя из факт. кол-ва столбцов //Тоесть, если frameposition=16, а столбцов 8, то выводятся элементы с 9 по 16 int frameposition=COLS; //Сколько кадров прошло int now_frames=0; //"Болванка" для столбца. Конкретный зажигается смещением. byte nowrow=B00000001; //----------------------------------------- //Собственно, строка для вывода: uint8_t string[lenght] = {B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011,\ //--------- B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011,\ //-------- B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011,\ //-------- B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011}; void setup() { Serial.begin(9600); pinMode(COLSlatchPin, OUTPUT); pinMode(COLSclockPin, OUTPUT); pinMode(COLSdataPin, OUTPUT); pinMode(COLSpoffpin, OUTPUT); pinMode(ROWSlatchPin, OUTPUT); pinMode(ROWSclockPin, OUTPUT); pinMode(ROWSdataPin, OUTPUT); pinMode(ROWSpoffpin, OUTPUT); pinMode(rst, OUTPUT); digitalWrite(COLSpoffpin, LOW); digitalWrite(ROWSpoffpin, LOW); } //Высчитывает номер элемента массива, //соответсвующего этому столбцу int elementnumber(int i) { return frameposition-(COLS-i); } void loop() { for (int i=0; i<COLS; i++) { //Отключаем питание на столбцах //digitalWrite(COLSpoffpin, HIGH); //Отключаем питание на строках //digitalWrite(ROWSpoffpin, HIGH); digitalWrite(ROWSlatchPin, LOW); //Передаём значения на строки shiftOut(ROWSdataPin, ROWSclockPin, MSBFIRST, string[elementnumber(i)]); digitalWrite(ROWSlatchPin, HIGH); //Подаём питание на строки //digitalWrite(ROWSpoffpin, LOW); digitalWrite(COLSlatchPin, LOW); //Передаём значения на столбцы shiftOut(COLSdataPin, COLSclockPin, MSBFIRST, nowrow<<i); digitalWrite(COLSlatchPin, HIGH); //Подаём питание на столбцы //digitalWrite(COLSpoffpin, LOW); //delay(1); /*Serial.print("COLS: "); Serial.println(B10000000>>i); Serial.print("ROWS: "); Serial.println(string[elementnumber(i)]); Serial.println(""); Serial.print("now_frames: "); Serial.println(now_frames); Serial.print("frameposition after: "); Serial.println(frameposition); */ } //Пересчитываем необходимые счётчики: now_frames++; if (now_frames==less_frame) { now_frames=0; frameposition+=offset; if (frameposition>lenght) {frameposition=COLS;} } }UPD3. Не работает :) Просто засветка настолько яркая, что я не сразу заметил,что это засветка, а не часть моего "рисунка". А вот в вашем алгоритме если добавить паузу в конец, то всё отлично. Конечный вариант полностью рабочего кода привожу постом ниже.
//Индикация динамическая. Развёртка по столбцам //---------------------------------------- //Выводы сдвигивого регистра //Регистр для строк int COLSclockPin = 11; //Пин подключен к SH_CP #define COLSdataPin 9 //Пин подключен к DS #define COLSlatchPin 7 //Пин подключен к ST_CP #define COLSpoffpin 5 //Регистр для столбцов int ROWSclockPin = 12; //Пин подключен к SH_CP #define ROWSdataPin 10 //Пин подключен к DS #define ROWSlatchPin 8//Пин подключен к ST_CP #define ROWSpoffpin 4 //RST пин общий #define rst 6 //----------------------------------------- //Описание матрицы #define COLS 8 //Физические столбцы #define ROWS 8 //Физические строки //----------------------------------------- //Описание анимации #define lenght 32 //Общая длинна строки для отображения (сколько столбцов) #define offset 1 //На сколько столбцов продвигать строку #define less_frame 80 //Сколько кадров пропустить перед след.шагом анимации (скорость анимации) //----------------------------------------- //Вспомогательные переменные: //frameposition фактически задаёт последний элемент массива, //который будет использован для вывода на матрицу //Первый элемент вычисляется, исходя из факт. кол-ва столбцов //Тоесть, если frameposition=16, а столбцов 8, то выводятся элементы с 9 по 16 int frameposition=COLS; //Сколько кадров прошло int now_frames=0; //"Болванка" для столбца. Конкретный зажигается смещением. byte nowrow=B00000001; //----------------------------------------- //Собственно, строка для вывода: uint8_t string[lenght] = {B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011,\ //--------- B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011,\ //-------- B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011,\ //-------- B11111111,\ B11111111,\ B11000000,\ B11000000,\ B11111111,\ B11111111,\ B00000011,\ B00000011}; void setup() { Serial.begin(9600); pinMode(COLSlatchPin, OUTPUT); pinMode(COLSclockPin, OUTPUT); pinMode(COLSdataPin, OUTPUT); pinMode(COLSpoffpin, OUTPUT); pinMode(ROWSlatchPin, OUTPUT); pinMode(ROWSclockPin, OUTPUT); pinMode(ROWSdataPin, OUTPUT); pinMode(ROWSpoffpin, OUTPUT); pinMode(rst, OUTPUT); digitalWrite(COLSpoffpin, LOW); digitalWrite(ROWSpoffpin, LOW); } //Высчитывает номер элемента массива, //соответсвующего этому столбцу int elementnumber(int i) { return frameposition-(COLS-i); } void loop() { for (int i=0; i<COLS; i++) { //Отключаем питание на столбцах digitalWrite(COLSpoffpin, HIGH); //Отключаем питание на строках digitalWrite(ROWSpoffpin, HIGH); digitalWrite(ROWSlatchPin, LOW); //Передаём значения на строки shiftOut(ROWSdataPin, ROWSclockPin, MSBFIRST, string[elementnumber(i)]); digitalWrite(ROWSlatchPin, HIGH); //Подаём питание на строки digitalWrite(ROWSpoffpin, LOW); digitalWrite(COLSlatchPin, LOW); //Передаём значения на столбцы shiftOut(COLSdataPin, COLSclockPin, MSBFIRST, nowrow<<i); digitalWrite(COLSlatchPin, HIGH); //Подаём питание на столбцы digitalWrite(COLSpoffpin, LOW); delay(1); //delay(1); /*Serial.print("COLS: "); Serial.println(B10000000>>i); Serial.print("ROWS: "); Serial.println(string[elementnumber(i)]); Serial.println(""); Serial.print("now_frames: "); Serial.println(now_frames); Serial.print("frameposition after: "); Serial.println(frameposition); */ } //Пересчитываем необходимые счётчики: now_frames++; if (now_frames==less_frame) { now_frames=0; frameposition+=offset; if (frameposition>lenght) {frameposition=COLS;} } }P.S. На строках выводы можно и не отключать.
Если не секрет, зачем вы подключили каждый регистр к своим линиям данных? - это два....
И три - можно и без OE (CS) обойтись, достаточно перед и после вывода nowrow выводить 0 в регистр катодов.
Откровенно говоря,я так и не понял ваш код. Поэтому, не в состоянии "только изменить функцию Draw" под использование регистров. Но из кода извлёк идею (правильно или нет?) которая сподвигла меня на написание своего рабочего кода.
Если не секрет, зачем вы подключили каждый регистр к своим линиям данных? - это два....
Что бы удобнее было "раздвигать" матрицу. Сейчас она одна - можно и каскадом обойтись. Но увеличу её в ширину на 1 матрицы, а в длинну на 10 и мне при каждом расширении нужно помнить, в каком порядке и сколько регистров у меня в каскаде. А теперь мне нужно следить за каскадами строк и столбцов отдельно.
И три - можно и без OE (CS) обойтись, достаточно перед и после вывода nowrow выводить 0 в регистр катодов.
Отключать выводы... Логичнее что ли.
Отключать выводы... Логичнее что ли.
Это не логичнее - это еще один лишний провод (кстати так же как и ресет). Вы же катоды через транзисторы (сборку) подключаете поэтому послать 0 в регистр это и есть отключить катоды. То есть танзистору без разницы, что вы базу подключили на землю, что оставили в воздухе - он все равно останется закрытым.
OE (CS) применяется в других ситуациях, когда нужно освободить линии к которым подключены Q0 - Q7 тоесть когда к этим линиям подключено, что то еще, у вас же кроме баз транзисторов больше ничего не подключено. И если уж на то пошло, то использовать OE (CS) даже более не верно, чем посылать в регистры 0, так как если на базах транзисторов создастся емкость, то в первом случае емкость будет медленно разряжаться и так же может появиться засветка, тогда как во втором емкость будет быстро разряжена на землю. И уж темболее данный способ не применим при использовании полевых транзистров без дополнительных подтягивающих резисторов.
Я так подозреваю, что можно обойтись и без ресета.
Виной засветкам являются монтажные ( и другие) емкости.
Попробуйте алгоритм (мультипликация построчная)
Паузой поиграться, возможно, и не потребуется.
Смысл в том, что строки должны зажигаться позже, а гаситься раньше столбцов.
Обратите внимание, что гасить и зажигать нужно не защелкой, а выбором (CS), т.е. в погашенном случае выходы регистра должны быть отключены.
Уважаемый AlexFisher
Вижу что вы хорошо разбираетесь в теме бегущих строк и управлении светодиодными матрицами.
Помогите за финансовую благодарность реализовать небольшой проект.
Я так подозреваю, что можно обойтись и без ресета.
Виной засветкам являются монтажные ( и другие) емкости.
Попробуйте алгоритм (мультипликация построчная)
Паузой поиграться, возможно, и не потребуется.
Смысл в том, что строки должны зажигаться позже, а гаситься раньше столбцов.
Обратите внимание, что гасить и зажигать нужно не защелкой, а выбором (CS), т.е. в погашенном случае выходы регистра должны быть отключены.
Уважаемый AlexFisher
Вижу что вы хорошо разбираетесь в теме бегущих строк и управлении светодиодными матрицами.
Помогите за финансовую благодарность реализовать небольшой проект.
Тема закрыта.