Динамическая индикация для самодельной LED матрицы. Как лучше реализовать?
- Войдите на сайт для отправки комментариев
Спаял такую вот матрицу из сорока светодиодов (5 строк, 8 столбцов)
Схематически её можно изобразить так
Принцип действия очень простой – управление столбами через выводы регистра. Управление строками – через усилительные транзисторы, подключенные к отдельным выводам микросхемы Atmega8 с прошитым Arduino бутлоадером.
Подскажите пожалуйста, как лучше реализовать динамическую индикацию для этой матрицы. Хочу сделать, что-то вроде бегущей строки.
Я думаю, вначале нужно написать функции включения./выключения для каждой строки, поскольку полноценное изображение здесь может быть сформировано только динамически (построчно)
В общем, алгоритм, как мне кажется, должен быть таким
1. Загрузить в регистр нужную последовательностей нулей и единиц для первой (в данном случае нулевой) строки.
2. Защелкнуть данные на выводах регистра
3. Включить строку
4. Сделать паузу (несколько миллисекунд).
5. Отключить строку
4. Перейти к следующей сроке (начать загружать для неё нужную последовательностей нулей и единиц).
Правильно ли я рассуждаю?
Подскажите пожалуйста, как сделать так, чтобы при таком алгоритме текст «бежал» вправо или влево???
Вот мой первый наброскок кода - только начал писать нужные функции. Оцените пожалуйста
#define Line_0 2 #define Line_1 3 #define Line_2 4 #define Line_3 5 #define Line_4 6 //Уровень HIGH на соответсвуещем выводе Arduino //позволяет вкл. элементы соответстующей строки #define SendData 11 //вывод передачи данных #define SendShift 12 //вывод передачи сдвига #define SendStore 8 //вывод "защелкивания" информации void setup(){ pinMode (Line_0, OUTPUT); pinMode (Line_1, OUTPUT); pinMode (Line_2, OUTPUT); pinMode (Line_3, OUTPUT); pinMode (Line_4, OUTPUT); pinMode (SendData, OUTPUT); pinMode (SendShift, OUTPUT); pinMode (SendStore, OUTPUT); //устанавливаем все выводы на выход } void loop(){ Line_0_EN(); Line_1_EN(); } void Line_0_EN() // LINE ENABLE (задействовать линию №0) { digitalWrite(Line_0, HIGH); } void Line_0_DIS() // LINE DISABLE (отключить линию №0) { digitalWrite(Line_0, LOW); } void Line_1_EN() // LINE ENABLE (задействовать линию №1 { digitalWrite(Line_1, HIGH); } void Line_1_DIS() // LINE DISABLE (отключить линию №1) { digitalWrite(Line_1, LOW); }
В принципе, да, вся динамическая индикация так и делается.
Только надо отделять друг от друга две задачи:
1)Вывод готового изображения на экран. В идеале, эту задачу можно повесить на прерывание от таймера, которое вызывается один раз в 2 мс и отрисовывает очередную строчку. Это даст частоту одновления в 100 гц - не сильно нагружает МК и одновременно хорошо смотрится для глаза.
2)Собственно подготовка изображения. Выделяете "видеопамять" (5 байт, по одному на строку идеально вам подходят) и в основном цикле ее заполняете необходимой картинкой. В частности, для того, чтобы текст бежал нужно просто применить операцию циклического сдвига к каждому байту, повторяя ее раз в некоторое время типа одной секунды.
Только учтите, что операция digitalWrite занимает достаточно долгое время, поэтому лучше писать в порты напрямую, иначе велик риск все процессорное время убить на отрисовку.
Выделяете "видеопамять" (5 байт, по одному на строку идеально вам подходят) и в основном цикле ее заполняете необходимой картинкой. В частности, для того, чтобы текст бежал нужно просто применить операцию циклического сдвига к каждому байту, повторяя ее раз в некоторое время типа одной секунды.
Я подключил эту LED матрицу к Atmega8. Как в этой микросхеме можно выделить "видеопамять"? Подскажите пожалуйста, откуда её выделять: из FLASH? из RAM? из EEPROM?
Только учтите, что операция digitalWrite занимает достаточно долгое время, поэтому лучше писать в порты напрямую, иначе велик риск все процессорное время убить на отрисовку.
А как можно "писать в порты напрямую"? Подскажите пожалуйста? Я думал, что проще функции digitalWrite ничего вообще нет! Ведь она только выдает 0 или 1 на определённом выводе. Или я ошибаюсь??? Подскажите тогда пожалуйста, какие функции мне лучше использовать для формирования и вывода изображения.
P.S. Заранее спасибо всем ответившим!
Я подключил эту LED матрицу к Atmega8. Как в этой микросхеме можно выделить "видеопамять"? Подскажите пожалуйста, откуда её выделять: из FLASH? из RAM? из EEPROM?
Специальной видеопамяти никакой, конечно нет.
Просто в программе объявить
После чего использовать videoMem[0] для содержимого первой строки, videMem[1] - для второй и тд.
А как можно "писать в порты напрямую"? Подскажите пожалуйста? Я думал, что проще функции digitalWrite ничего вообще нет! Ведь она только выдает 0 или 1 на определённом выводе. Или я ошибаюсь???
http://arduino.ru/Tutorial/Upravlenie_portami_cherez_registry
Функции, конечно, простые, но кроме написания числа в порт им еще надо
1)Преобразовать номар порта в понимании ардуино в регистр и номер бита в понимании атмеги.
2)Проверить, нет ли на "порту" ШИМа
3а)Отключить его если что.
4)Проверить, что мы хотим - писать или читать.
В результате там, где процессору нужно реально один или четыре тактат (в зависимости от того, надо ли нам сохранить остальное содержимое регистра), digitalWrite тратит по самым скромным подсчетам минимум в десять раз медленне.
Подскажите тогда пожалуйста, какие функции мне лучше использовать для формирования и вывода изображения.
Для формирования - смотря что вы хотите, но при таких объемах проще "в голове прикинуть"
Для вывода - http://arduino.ru/Reference/Library/SPI
Просто в цикле выводить по одному байту из videoMem[i] в SPI
Я подключил эту LED матрицу к Atmega8. Как в этой микросхеме можно выделить "видеопамять"? Подскажите пожалуйста, откуда её выделять: из FLASH? из RAM? из EEPROM?
Можно в любой. Но наиболее просто/естественно/быстродействие - конечно в RAM.
А вообще, похоже вы себе тут представили что-то намного более страшно-сложное чем есть на самом деле.
Никакой особой "видеопамяти" в Atmega8 естественно нет. Выделение - это вы просто объявляете массив. И у вас есть две части програмы. Одна занимаете тем что выводит содержимое этого массива на диоды. А другая - тем что меняет содержимое этого массива когда нужно (по нажатию кнопок, по датчикам и т.п.)
Так что, в данном случае "видео-память" - это скорее термин который существует только в голове, а не железе.
Вообщем
это и есть "выделили видео-память".
А как можно "писать в порты напрямую"? Подскажите пожалуйста? Я думал, что проще функции digitalWrite ничего вообще нет! Ведь она только выдает 0 или 1 на определённом выводе. Или я ошибаюсь???
И да и нет. Вы не ошиблись в том что делает digitalWrite "с точки зрения пользователя", но ошиблись в том что поставили знак равенства между "простое поведение,просто пользоватся" и "просто устроено внутри".
Где читать? В шапке сайта, есть ссылка Программирование, там есть раздел "Базовые и полезные знания, необходимые для успешного программирования под платформу Arduino". В котором, в свою очередь Прямое управления выходами через регистры микроконтроллера Atmega
И посмотрите какие-там рядом статьи /примеры есть. Еще парочку вам пригодятся - про сдвиговые, к примеру.. Этим вы избежите будущих посыланий в поиск и документацию.
Но... вы все равно попробуйте поиском тоже попользоватся. И сдвиговые (по имени микрухи и по словам "множим выходы") и матрицы диодов (слова "LED матрица", "динамическая индикация") - не раз обсуждались. Покопайтесь - возможно многие будущие вопросы сами отпадут :)
А еще, по поводу прямой записи в регистры можете почитать AVR. Учебный курс. Устройство и работа портов ввода-вывода ... Тоже полезно для лучшего понимая всей этой кухни.
И возвращаясь к digitalWrite, после прочтения всего что выше, как видите включение ноги в нужно состояние требует выставить правильный бит, в правильном порту. Но... на разных камнях эти порты/биты разные. А скетчи - универсальные. digitalWrite(13,HIGHT) одинаково отработает и на уно и на меге. Именно за счет "интелектуальности" фунции digitalWrite. Которая, в зависимости от платы, сама решит какой порт/бит нужно выставить. Но за эту универсально/разумность/легкую преносимость - нужно платить. Тактами процессора и невозможностью поменять состояние сразу нескольких ног одновременно.
Поэтому когда на важна скорость - приходится откадываться от digitalWrite и самому смотреть в даташит и пин-маппинг что чему соотвествует. Выигрываем в скорости, но теряем в универсальности (при переходе на другой камень - нужно будет править скетч) и легкости читания кода.
вот такая статья оч понравилась, все там доходчиво и ясно, правда только с бОльшей матрицей, но принцип один...
Мне кажеться, что оптимальный вариант - использование двух массивов битов: один - для строк, второй - для столбцов.
Только вот, как правильно объединить их в одно целое для вывода определенного символа на матрицу?
Ведь один байт (для выбора нужных столбцов) очевидно должен передаваться на регистр по SPI, второй байт (для выбора нужной строки) будет неполным! И передаваться он должен напрямую!
В Atmega8 для выбора нужной строки задействованы такие выводы
Как использовать операцию сдвига, чтобы он происходил не до конца, то есть не выходил за пределы задействованных выводов?
Может кто-нибудь подскажет?
вот такая статья оч понравилась, все там доходчиво и ясно, правда только с бОльшей матрицей, но принцип один...
Спасибо за ссылку! Статья действительно хорошая. Но если действовать по тому же принципу, придётся использовать два регистра сдвига (один для столбцов, другой - для строк).
Я же хочу довести до ума пример, где нужные светодиоды в столбцах матрицы выбираються через регистр сдвига, а нужная строка - появлением логической единицы на соответствующем выводе Atmega8.
Перебирать строки, я думаю, проще всего с помощью массива
а вот как вставлять в эти строки нужные значения битов, так чтобы символы могли "бежать", я пока не додумался.
Допустим, символ - это поле 5х5 точек. Значит мне нужно создать массив типа byte. Но вот как "запихивать" в него нужные значения? Нужно создавать какую-то таблицу знакогенератора? Подскажите пожалуйста!!!
вот такое я писАл
"занкогенеротор" так сказать в строках 4 и 5
ну а запихивать в него типа
matrix[1]=5;
matrix[2]=8;
ну и т.д. это в моем скетче...
Вам в регистр нужно отправлять только одно слово (1 или 2 или 4 или 8 или 16 и т.д. т.е. перебирайте столбцы в двоичном коде), а на строки с выводов дуньки, то есть правильно мыслите... т.е. создаете цикл в котором поочереди перебираются столбцы и отправляются в регистр, ну и во время выбора первого столбца отправляйте на выводы дуньки из массива знакогенератора первое слово, во время выбора второго столбца - второе слово знакогенератора... ну и т.д...
Написал две функции - одну для вывыда столбцов, другую - для строк. Код стал выгледеть так:
Но компилятор почему-то ругаеться на строки 28, 29! Он выдает: too few arguments...
Может быть это из-за того, что я "запихнул" одну функцию в другую??? Подскажите пожалуйста, может кто-нибудь знает???
Ради интереса - посмотрите на строку 44 и попробуйте найти различия со строкой 29.
Их там два. Компилятор оба этих отличия находит, теперь дело за вами.
Далее - что должны символизировать фигурные скобки в строках 28 (открывающая) и 30 (закрывающая)?
И почему оператор в строке 28 не завершен, как и полагается, символом ";"? Чего вы хотели этим добиться? Конструкция настолько необычная (наверняка, кроме ругательства на слишком малое число аргументов у вас далее еще есть сообщение что-то вроде "error: expected `;' before '{' token"), что без ваших объяснений трудно понять ее, ну и, соответственно, порекомендовать правильный вариант исправления (чтобы не только синтаксически верно было, но и задачу решало).
Спасибо за то, что указали на ошибки!
Я поправил код! Теперь он выглядит так:
Теперь компилятор не ругается, но нужный символ на матрице все равно не появляеться!
P.S.: Я очень детально прокомментировал свой код, пожалуйста, кому не лень, посмотрите, что в нём может быть неправильно!
Одно непонятно, зачем смешивать использование DDRD, digitalWrite и т.п. Это называется искать проблемы
digitalWrite используеться для трех выводов порта С. Разве он имеет какое-то отношения к порту D, для которого я использовал прямую запись в порт
Разве применение такого способа в паре с digitalWrite может привести к неправильному компилированию? Мне кажеться, ошибка в чём-то другом.
Может быть кто-то из специалистов подскажет, что я написал в коде неправильно?
Может быть кто-то из специалистов подскажет, что я написал в коде неправильно?
void loop() {
stolbetsLED; //загружаем значения в регистр для отображения столбца
strokaLED; // вкл. соответствующую строку
delay(20); //кратковременная пауза // и т.д. загружаем в регистр значения, включаем нужную строку... }
Всё не смотрел, но функции надо вызывать с аргументами, если их нет, то скобка всё равно должна быть.
stolbetsLED(); - вот так.
и ваще, вы не правильно написали функции...
void
stolbetsLED(
byte
collsArray[],
byte
collsCounter){
так вот этот аргумент collsArray совсем не тот, что вы описали выше, это заново созданный указатель, не имеющий отношения к предыдущему.
...и ваще, вы не правильно написали функции...
так вот этот аргумент collsArray совсем не тот, что вы описали выше, это заново созданный указатель, не имеющий отношения к предыдущему.
А куда же тогда он указывает? И как обратиться к ранее созданной переменной (или массиву)? Или это невозможно? Подскажите пожалуйста!