DMD_STM32 - версия библиотеки DMD для СТМ32Дуино
- Войдите на сайт для отправки комментариев
Вс, 05/05/2019 - 15:12
Редакция библиотеки DMD.h для p10 светодиодных матриц - под платы СТМ32 в рамках проекта Arduino_STM32
Редакция библиотеки DMD.h для p10 светодиодных матриц - под платы СТМ32 в рамках проекта Arduino_STM32
Просьба не цитировать этот пост, чтобы я мог потом его редактировать
Код библиотеки DMD_STM32 на гитхабе https://github.com/board707/DMD_STM32
О использовании фонтов в формате Adafruit GFX и создании своих шрифтов из TTF файлов читайте в сообщении #2
О фонтах с национальными символами - в сообщении #6
++++++++++++++++ обновление ++++++++++++++++++++++++++
Версия 0.9.0 от 15.09.2022
Добавлена поддержка DMA для RGB матриц (только STM32F4)
Добавлена поддержка контроллера RP2040 (только RGB)
Подробнее - в сообщении #459
Версия 0.8.1 от 14.09.2022
Bug-fix для v0.8.0
Подробнее - в сообщении #457
Версия 0.8.0 от 10.07.2022
Добавлена поддержка STM32F4 плат
Подробнее - в сообщении #451.
Версия 0.7.0 от 19.02.2022
Добавлена поддержка уличных RGB матриц с многоcтрочным сканированием 1/2 1/4
Подробнее - в сообщении тут. Адрес гитхаб тот же.
Версия 0.6.11 от 15.01.2022
https://github.com/board707/DMD_STM32/tree/dev-V2
Добавлена возможность задавать разные таймеры для OE PWM. Исправлены некоторые баги.
Подробнее в сообщении #408
Версия 0.6.8 от 05.12.2021
https://github.com/board707/DMD_STM32/tree/dev-V2
Переделал контроль яркости для RGB панелей через ШИМ, синхронизированный с основным циклом сканирования матриц.
Версия 0.6.3 от 18.07.2021
Добавлен режим 1битного цвета на RGB для табло и вывесок, более эффективно переписан вывод текста, соединение панелей зигзагом.
https://github.com/board707/DMD_STM32/tree/v0.6.3
Релиз v0.5.0 от 18.05.2021
По сути на 70% новая библиотека, поэтому завел на гитхабе новую ветку (branch) dev-V2 и изменил название базового класса с DMD_STM32 на DMD_STM32a. Главное изменение - добавлена поддержка многоцветных RGB панелей.
Ссылка на гитхаб - https://github.com/board707/DMD_STM32/tree/dev-V2
Подробнее - в сообщении #319
Версия 0.4.2 от 15.06.20 - исправлена работа функции stepMarquee() на инверсных дисплеях, увеличена максимальная длина бегущей строки с 256 символов до значения, заданного пользователем в дефайне MAX_STRING_LEN, увеличена дефолтная частота SPI до 9 МГц
Версия 0.4.1 от 23.10.19 включает:
- все возможности , аналогичные оригинальной библиотеке DMD.h от Freetronics - вывод символов, текстов, бегущей строки
Добавлено:
- поддержка платы СТМ32 "blue pill"
- поддержка фонтов в формате Adafruit GFX. в том числе национальных алфавитов(для новых языков необходимо только дописать функцию перекодировки текста из UTF в кодировку фонта)
- вертикальный скроллинг текста для СТМ и АВР
- на СТМ32 возможно одновременное использование двух наборов матриц на двух SPI каналах
- На СТМ существенно уменьшена загрузка процессора при выводе изображения на матрицы за счет использования DMA. что позволяет подключать большее число панелей либо задействовать МК для других задач
+++++++++++++++++++++++++++++++++++++++++++++++
В качестве базы использован незаконченный проект DMDSTM https://github.com/mozok/DMDSTM Попытки связаться с автором успеха не дали. От проекта DMDSTM в настоящую библиотеку перекочевала возможность вывода сообщений кириллицей в кодировке UTF8 прямо из Ардуино ИДЕ - при выборе измененных шрифтов, начинающихся с UkrRus...
Использование библиотеки аналогично оригинальной DMD.h, за исключением контруктора - он требует указания используемых пинов и обьекта SPI:
Пины CLK и R_DATA жестко определены используемым каналом SPI, их указывать не нужно:
SPI_1: CLK = PA5
R_DATA = PA7
SPI_2: CLK = PB13
R_DATA = PB15
Благодаря тому, что пины теперь являются переменными класса DMD, появилась возможность вызывать несколько эземпляров, в частности на STM32F103, где два независимых канала SPI - можно повесить две цепочки матриц и управлять ими отдельно. см. пример double_dmd и видео https://www.youtube.com/watch?v=OCpLTLpwgRI
Подключение описано в README. Обратите внимание на сноску Исключения (Exclusions) под табличкой пинов. не используйте ноги PB3 PB4 PA11 PA12. Оттестированные комбинации пинов можно подсмотреть в примерах.
Любые замечания и пожелания приветствуются!
Почта для связи dd собака jwee.ru
Прикрутил к библиотеке поддержку фонтов в формате GFX_Font от либы Adafruit GFX. Этот формат хорош тем, что в самой библиотеке куча фонтов, и , главное, можно одним движением конвертить в этот формат фонты TTF, которых сотни в инете. В том числе и в национальных кодировках. Например можно брать фонты с кириллицей из винды.
Использование фонта Adafruit GFX, например FreeMono9pt7b.h:
1. Копируем фонт в папку скетча
2. Открываем файл фонта, проматываем в конец и видим там такую структуру:
const GFXfont FreeMono9pt7b PROGMEM = { (uint8_t *)FreeMono9pt7bBitmaps, (GFXglyph *)FreeMono9pt7bGlyphs, 0x20, 0x7E, 18 };запоминаем имя структуры - FreeMono9pt7b - это имя фонта для указания в программе
3. В коде подключаем .h файл фонта и создаем обьект фонта, передавая ему ссылку на структуру GFXFont из файла. Далее выбираем этот фонт как текущий методом selectFont() и можно пользоваться :
Результат -Видео - выводим текст фонтом FreeSerifBold9pt7b из Adafruit_GFX:
https://youtu.be/jRa607nLxqI
======
Для создания новых фонтов из файлов TTF воспользуйтесь утилитой fontconvert из состава библиотеки Adafruit-GFX. Это утилита командной строки, синтакс вызова:
где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях, first_char и last_char - диапазон символов, который будет помещен в выходной фонт. Последние два параметра необязательны, они полезны, если вы хотите сгенерить компактный фонт, например, только с цифрами.
Вывод программы представляетят готовую структуру формата GFXFont и массивы символов(см выше), которые остается поместить в .h файл.
Подробнее о фонтах adafruit_GFX
Спасибо, слежу за проектом ;) BTW, только под STM32? Или компилится под другие МК?
только под STM32? Или компилится под другие МК?
пока нет. Изначальная цель проекта была именно порт под СТМ :) ... и еще я убил немало времени, чтобы получить возможность одновременного запуска двух DMD обьектов... поэтому о возврате на АВР пока не думал.
Но в принципе, специфично СТМ-ского кода там совсем чуть-чуть - думаю настроить условную компиляцию будет не слишком сложно.
DIYMan , я победил Unicode :)
https://youtu.be/9xUB8-jk5Tc
Строка выведена шрифтом Comic9pt из комплекта Windows7. Фонт преобразован Адафрутовским fontconvert прямо из виндовского TTF. Получаются фонты, сразу готовые к подключению к библиотеке. Нагенерить кучу фонтов совершенно не проблема, причем не только с кириллицей.
Хотя конечно работа с UTF8 со стороны пользовательского скетча у меня пока реализована не слишком изящно - нужно пропускать строчки через некую функцию utf8_rus(), которая преобразует 16-битные UTF8 коды Ардуино ИДЕ в верхнюю половину 8-битной ASCII таблицы. В будущем, конечно, хотелось бы чтобы пользователь передавал строки в методы класса DMD -и они внутри автоматом перекодировались.
Зато в остальном схема более менее универсальна - меняя функции перкодировки и добавляя фонты с нужным чарсетом, можно добавить к DMD поддержку любого национального алфавита, символы которого кодируются 16-битным Юникодом.
Фонты с кириллицей и другими национальными алфавитами.
Большинство TTF фонтов для национальных символов используют Unicode, при этом латиница расположена в диапазоне 0х20 - 0х7E, а буквы кириллицы А-Яа-я имеют коды 0х410-0х44F. Из этого возникает проблема - Формат GFX_font хранит битмапы символов сплошным массивом, и чтобы охватить разом латиницу и кириллицу, нужно либо оставлять в фонте кучу (около 850-ти) неиспользуемых символов между латиницей и кириллицей, либо кириллицу перекодировать поближе к латинице.
Для экономии места мы используем другой подход - каждый обьект DMD_GFX_Font может содержать ссылки на несколько (в данном случае два) отдельных массива символов - для латиницы и для кириллицы. А в момент вывода на матрицу конкретного символа библиотека сама выбирает нужный фонт.
Для создания подобного фонта из TTF-файла нужно использовать утилиту fontconvert дважды (подробнее о использовании fontconvert - в сообщении #2):
далее необходимо открыть второй файл (tt.out2) -с кириллицей - и найти в нем две структуры(привожу только заголовки) :
const GFXglyph FreeMono16pt8bGlyph[] PROGMEM = { const GFXfont FreeMono16pt8b PROGMEM = {дело в том, что имена этих структур получились одинаковыми и для латиницы и для кириллицы. Чтобы к ним можно было обращаться по отдельности - переименуем вторую пару, добавив к именами приставку _rus. например:
const GFXglyph FreeMono16pt8bGlyph_rus[] PROGMEM = { const GFXfont FreeMono16pt8b_rus PROGMEM = {Теперь содержимое обоих файлов необходимо скопировать в один и сохранить его как файл .h - например FreeMono16.h
Использование двойного фонта с библиотекой DMD_STM32:
параметры в скобках - сначала ссылки на структуры GFXFont для латиницы и кириллицы.
0x80 - однобайтный код первого национального символа во второй части фонта
13- сдвиг фонта по вертикали в пикселях
Не знаю, актуально ли это для STM, но на AVR замена digitalWrite() на прямое манипулировние регистрами экономит такты, а рисование из PGM-строки - экономит RAM. Если надо в этом плане подмогнуть - пиши в email, расскажу чего как.
Гриш, за предложение спасибо, но вроде пока не нужно. Работу с железом в этой либе я взял готовую из стандартной библиотеки DMD от Freetronics и пока улучшать не планировал. А что касается экономии памяти - все фонты там и так в ПРОГМЕМе
С ресурсами на СТМ особо проблем нет - код, что на видео, использует 17% флеша (половина из которых - фонты) и 12% оперативки. Всего.
PGM-строка - это которая не перегружается в RAM. "Войну и Мир" гнать, например, бегущей строкой.
А замена digitalWrite на несколько простых конструкций давала мне выигрыш по скорости втрое на AVR-ке.
PGM-строка - это которая не перегружается в RAM. "Войну и Мир" гнать, например, бегущей строкой.
Спасибо, если что - это я умею :) но у меня пока задач для такой бегущей строки нет - матрицы я просто поиграться купил. :)
А замена digitalWrite на несколько простых конструкций давала мне выигрыш по скорости втрое на AVR-ке.
это я понимаю - и это интересно. Надо будет попробовать. Оригинальная библиотека, скажем так, не слишком эффективно написана, даже мне это очевидно.
DIYMan , я победил Unicode :)
https://youtu.be/9xUB8-jk5Tc
Супер, браво! Теперь осталось сделать компилируемым под 328-ю мегу - и нахер заброшу штатную DMD ;) Осилим? ;)
матрицы я просто поиграться купил. :)
Не, это уже не просто поиграться - ты себе не представляешь, что ты сделал, и какой секас со шрифтами был. ОЧЕНЬ советую сделать кросскомпилируемым под разные платформы (а не только под конкретный камень), и подумать над дальнейшим развитием. Всё - не вкладывай, рекламщики заплатят, если что ;)
Ещё вариант развития - GUI-редактор, и можно продавать ;)
З.Ы. Короче: развивай, если нужна мал-мала помощь - помогу, чем смогу. Пока не скачивал с гитхаба, но обязательно попробую - в наличии есть три матрицы. Кстати за них: надеюсь, ты в курсе, что схемотехника у матриц разных цветов может быть разная? Столкнулся с тем, что код, который на красной матрице показывал текст нормально, на зелёной - показывал инверсный текст. С точки зрения переносимости кода конкретной прошивки - желательно в библиотеке предусмотреть флаг в конструкторе, типа "инверсная логика" ;) Когда допиливал DMD - так и делал.
Теперь осталось сделать компилируемым под 328-ю мегу - и нахер заброшу штатную DMD ;) Осилим?
думаю да
и пока не скачивай - поддержки фонтов там пока нет
Кстати за них: надеюсь, ты в курсе, что схемотехника у матриц разных цветов может быть разная? С точки зрения переносимости кода конкретной прошивки - желательно в библиотеке предусмотреть флаг в конструкторе, типа "инверсная логика" ;)
да, слышал, но поскольку у меня матрицы только зеленые - пока не сталкивался. В библиотеке, кстати, есть переключатели прямого и инверсного режима...
а как тогда распространять? :)
В библиотеке, кстати, есть переключатели прямого и инверсного режима...
Ты имеешь в виду флаги отрисовки? Это не то, потому что применяется _по_месту_. А если мне нужно на букве текста отобразить звезду, скажем? Я просто в коде пишу, образно - draw(...,GRAPHICS_NORMAL), потом поверх draw(,,,GRAPHICS_INVERSE). И если я поменяю матрицу на красную, то окажется, что теперь текст - инверсный, а звезда - горит, поэтому надо в тыще мест поменять местами флаги отрисовки. Я же говорю - за общий флаг отрисовки, передаваемый в конструкторе, например ;)
а как тогда распространять? :)
Как обычно - основные плюшки - свободно, вкусности - за донат или лицензию. Так, чисто на поддержку штанов - конкуренция в этой нише есть, надо приложить массу усилий, чтобы сделать вкусный продукт. Но окупить свою работу - стоит, считаю. Потому как это совершенно чётко - рекламщики, там деньги есть, и не стоит бесплатно халявщиков плодить ;)
З.Ы. У меня матрицы красные, кста. Могу протестировать на Uno, если что ;) Blue pill щас нету, есть парочка взрослых STM - V и Z серий.
Я протестирую, если че.... 13 вернусь с Питера и в легкую, десяток 103 как раз китайцы подвезли.
Приветствую, пробовал данную библиотеку с инверсным красным модулем (инверсию поменял через библиотеку, но проблемы те же), имею следующие проблемы:
1. Если яркость находится в диапозоне от 258 до 65534 имеются моргания пикселей что горят выше. Если 257 или 65535 то проблемы нету. В библиотеке Евгения Мозка точно самое.
2. Очень моргает, у Евгения Мозака так не моргает.
С таким же успехом dmd2 SPI и Software версию сделал со всеми платформами, но проблема под номером 1 осталась ;(. Самое интересное что на AVR такой проблемы нету.
На руках ещё кучка контроллеров onbon bx-5ul, там стоит китайский GD32, он же разогнанный STM32, сделал трассировку платы и вывод такой, там используется софтварный SPI, кварц на 25мгц, последних два поменял на blue pill'овские и софтварная версия DMD2 работает. На заметку, там линии OE, A, B, SCLK, на оба порта используются те же, а вот R1 и CLK для каждого свой. Ну это так на будущее)
Все проверки делал на первом SPI PB5
Был бы ослик может избавился от первой проблемы ;(
Приветствую, пробовал данную библиотеку с инверсным красным модулем (инверсию поменял через библиотеку, но проблемы те же), имею следующие проблемы:
1. Если яркость находится в диапозоне от 258 до 65534 имеются моргания пикселей что горят выше. Если 257 или 65535 то проблемы нету. В библиотеке Евгения Мозка точно самое.
2. Очень моргает, у Евгения Мозака так не моргает.
b8hri11, приветсвую
Что касается проблемы 1 - насколько я вижу по форумам, слабые мерцания незасвеченных пикселей - это общая проблема библиотек DMD и DMD2 при использовании контроллеров с напряжением 3.3в. То что это связано со скважностью PWM - интересное наблюдение, надо будет проверить на АВР, может там так же?
По п 2 - поясните, что именно "очень моргает" ?
Что касается проблемы 1 - насколько я вижу по форумам, слабые мерцания незасвеченных пикселей - это общая проблема библиотек DMD и DMD2 при использовании контроллеров с напряжением 3.3в. То что это связано со скважностью PWM - интересное наблюдение, надо будет проверить на АВР, может там так же?
на китайском контроллере стоят 74hc245 они сигналы в 5 вольт преобразуют, но даже так видно проблему. На оригинальной плате там вообще нету мерцания даже на камеру. R1 обоих разъемов подключены на PB4 и PB5.
Сейчас ещё попробую на blue pill глянуть как оно будет работать.
2 - поясните, что именно "очень моргает" ?
Видно глазу как мерцает, как эфект строб, больно смотреть даже. Если в оригинале у Евгения исправить период таймера с 4000 до 1000 или 2000 то даже на камеру не видно мерцания.
Видно глазу как мерцает, как эфект строб, больно смотреть даже. Если в оригинале у Евгения исправить период таймера с 4000 до 1000 или 2000 то даже на камеру не видно мерцания.
это в высшей степени странно - если вы сравните две библиотеки, код вывода сигнала на матрицу я взял у Евгения практически без изменений, добавил только выбор SPI канала и инициализацию SPI. Кстати. без этого код Евгения у меня вообще не работал. А у вас его код работает без изменений?
В моих тестах я особых мерцаний не вижу, хотя может просто не понимаю, куда смотреть ... это первые матрицы, которые я держу в руках
степени странно - если вы сравните две библиотеки, код вывода сигнала на матрицу я взял у Евгения практически без изменений, добавил только выбор SPI канала и инициализацию SPI. Кстати. без этого код Евгения у меня вообще не работал. А у вас его код работает без изменений?
В моих тестах я особых мерцаний не вижу, хотя может просто не понимаю, куда смотреть ... это первые матрицы, которые я держу в руках
примеры конечно же доработать пришлось, но содержание примерно то же что в вашей версии.
Фантомных пикселей нету если в место analogWrite или pwmWrite используется digitalWrite, но тогда яркость вырвиглазная)
#include <SPI.h> //SPI.h must be included as DMD is written by SPI (the IDE complains otherwise) #include <DMD_STM32.h> // //#include <TimerOne.h> // #include "SystemFont5x7.h" #include "Arial_black_16.h" SPIClass SPI_2(1); //Fire up the DMD library as dmd #define DISPLAYS_ACROSS 1 #define DISPLAYS_DOWN 1 DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN); /*-------------------------------------------------------------------------------------- Interrupt handler for Timer1 (TimerOne) driven DMD refresh scanning, this gets called at the period set in Timer1.initialize(); --------------------------------------------------------------------------------------*/ void ScanDMD() { dmd.scanDisplayBySPI(SPI_2); } /*-------------------------------------------------------------------------------------- setup Called by the Arduino architecture before the main loop begins --------------------------------------------------------------------------------------*/ void setup(void) { SPI_2.begin(); //Initialize the SPI_2 port. SPI_2.setBitOrder(MSBFIRST); // Set the SPI_2 bit order SPI_2.setDataMode(SPI_MODE0); //Set the SPI_2 data mode 0 SPI_2.setClockDivider(SPI_CLOCK_DIV64); // Use a different speed to SPI 1 SPI.setModule(1); afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY); // release PB3 and PB5 afio_remap(AFIO_REMAP_SPI1); gpio_set_mode(GPIOB, 3, GPIO_AF_OUTPUT_PP); gpio_set_mode(GPIOB, 4, GPIO_INPUT_FLOATING); gpio_set_mode(GPIOB, 5, GPIO_AF_OUTPUT_PP); pinMode(SPI2_NSS_PIN, OUTPUT); //clear/init the DMD pixels held in RAM dmd.clearScreen( true ); //true is normal (all pixels off), false is negative (all pixels on) // Led Panel setup Timer3.setMode(TIMER_CH1, TIMER_OUTPUTCOMPARE); Timer3.setPeriod(3000); // in microseconds Timer3.setCompare(TIMER_CH1, 1); // overflow might be small Timer3.attachInterrupt(TIMER_CH1, ScanDMD); dmd.setBrightness(100); }Если есть у кого ослик, посмотрите как выглядят сигналы digitalWrite и analogWrite на AVR и на STM32. Там думаю будет понятно.
b8hri11 -насчет "моргает" - кажется понял, о чем вы. Если поставить brightness = 65535, то фантомных пикселей нет, но вся надпись иногда моргает - причем не ритмично, а то чаще, то реже, как будто наводки идут. Вы это имели в виду? Спасибо, попытаюсь разобраться.
Правда не могу проверить, было ли такое на коде Евгения, у меня даже с инициализацией SPI его код почему-то не работает.
DIYMan - "портировал обратно" свой код на Атмегу328 :) Русские фонты работают.
Хотел посоветоваться. Не могу решить, как в библиотеке поступить с перекодировкой. Оставить это на юзера - чтобы каждый сам добавлял в скетч функцию utf8_rus() , по аналогии с тем, как в DMD заставлют пользователя вставлять в скетч инициализацию таймера. Или же встроить перекодировку в функции библиотеки?
Первый вариант выглядит несколько кустарно. Зато добавить другую кодировку вместо кириллицы - не проблема, заменил функцию в скетче да и все.
Из общих соображений:
1. Перекодировка из многобайтовой в однобайтовую кодировку, очевидно, операция с потерей информации. Зависит от используемой кодовой страницы. Соотвественно, если проект планируется как международный, эту операцитю следует либо делать вовне библиотеки, либо уж учиться правильно обрабатывать все 65536 юникодных символов, а не только символы кириллицы.
2. Обработка строки и отображение ее на устройстве - совершенно не связанные друг с другом операции. Вряд ли их целесообразно делать в одной библиотеке.
Т.е. оба соображения з-а то, чтобы в библиотеке перекодировки не делать.
И еще: с моей точки зрения, кустарно - это когда библиотека работы с "железом" заточена именно на кириллицу и не позволяет относительно простыми средтвами применять ее для других наборов символов.
Правда не могу проверить, было ли такое на коде Евгения, у меня даже с инициализацией SPI его код почему-то не работает.
SPI.setModule(1); // тут spi выбрать нужно, если он не первый, то второй и код ниже не нужен
34
afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY); // release PB3 and PB5
35
afio_remap(AFIO_REMAP_SPI1);
36
gpio_set_mode(GPIOB, 3, GPIO_AF_OUTPUT_PP);
37
gpio_set_mode(GPIOB, 4, GPIO_INPUT_FLOATING);
38
gpio_set_mode(GPIOB, 5, GPIO_AF_OUTPUT_PP);
39
это все не то. Это вы зачем-то "перемапили" SPI_1 на PB3-PB5, а если пользовать "родные" пины PA5 - PA7, то этот код не нужен. Как конфигурировать пины - это я все в курсе .
Вы мне так и не ответили на вопрос, в чем заключается "стробоскоп", который есть на моем коде и которого нет у Евгения? - Хаотичные редкие помаргивания символов - иногда раз в 2-3 секунды, иногда пару раз в секунду? - Оно?
Т.е. оба соображения з-а то, чтобы в библиотеке перекодировки не делать.
Пожалуй соглашусь :) Тем более что так и мороки меньше. Получается очередная XXXX-ая по счету библиотека руссуфикации чего-либо.
stm32duino ?
DIYMan - "портировал обратно" свой код на Атмегу328 :) Русские фонты работают.
Хотел посоветоваться. Не могу решить, как в библиотеке поступить с перекодировкой. Оставить это на юзера - чтобы каждый сам добавлял в скетч функцию utf8_rus() , по аналогии с тем, как в DMD заставлют пользователя вставлять в скетч инициализацию таймера. Или же встроить перекодировку в функции библиотеки?
Первый вариант выглядит несколько кустарно. Зато добавить другую кодировку вместо кириллицы - не проблема, заменил функцию в скетче да и все.
Я за вариант номер 2. Составить документ по соглашению кодовой страницы шрифта, и всё. Впихнуть невпихуемое невозможно, рефлексировать на всех желающих - також.
Я за вариант номер 2. Составить документ по соглашению кодовой страницы шрифта, и всё. Впихнуть невпихуемое невозможно, рефлексировать на всех желающих - також.
нифига не понял, сорри. Какой документ по соглашению? Хелп с описанием как добавлять фонты? Или речь о коде?
Мне кажется, после тщательного обдумывания, как именно следует "составить документ", чаша весов склонится в пользу первого варианта ))).
Мне кажется, после тщательного обдумывания, как именно следует "составить документ", чаша весов склонится в пользу первого варианта ))).
В качестве подготовки - напишу о работе с национальными фонтами
Изначально задача ставилась так - прикрутить к DMD-матрицам какие-нибудь распространенные фонты. После изучения вариантов я выбрал формат GFXFont из библиотеки Adafruit_GFX. Формат простой и ясный и хорошо документированный, и при этом мощный. Но что важнее - в этот формат можно одним движением конвертировать любые TTF и OTF фонты при помощи родной утилиты fontconvert от того же Адафруита. Фонтов TTF OTF в инете десятки тысяч, для лыбых наборов национальных символов. Многие фонты бесплатны, в том числе и для использования в коммерческих проектах. Для примера ссылка на сайт :
https://www.fontspace.com/
Как человек ленивый - я сразу поставил себе условие, что фонты должны подключаться "как есть", без редактирования и сложных перекодировок. Большинство TTF фонтов используют Unicode для национальных символов, при этом латиница расположена в диапазоне 0х20 - 0х7E, а буквы кириллицы А-Яа-я имеют коды 0х410-0х44F. Из этого возникает проблема - Формат GFX_font хранит битмапы символов сплошным массивом, и чтобы охватить разом латиницу и кириллицу, придется либо оставлять в фонте кучу (около 850-ти) неиспользуемых символов между латиницей и кириллицей, либо кириллицу перекодировать поближе к латинице. Но мы же решили, что ничего перекодировать не будем. Поэтому делаем проще - для каждого фонта в библиотеке поддерживается несколько обьектов GFX-Font, каждый на свой диапазон кодов. Для латиницы и кириллицы получаем два отдельных компактных обьекта GFX_Font, в каждом из которых только нужные символы, а в момент вывода на матрицу конкретного символа библиотека сама выбирает нужный фонт.
Остается еще проблема, что в Ардуино ИДЕ для кириллицы используется совсем другая кодировка, которая вроде тоже Unicode, но коды другие и даже порядок символов не тот. Чтобы не трахаться (извините) с двухбайтовыми символами, строчки текста из Ардуино ИДЕ я перекодирую из 16битных в 8битные, отображая коды в верхнюю половину таблицы ASCII. При этом конкретные коды символов, используемые в библиотеке и в фонте - в общем-то не имеют значения, лишь бы лпростенькая программа перекодировки отображала одни коды в другие. Результат перекодировки должен иметь тот же порядок символов, что и в фонте GFX, например я в своей библиотеке поместил символы А-Яа-я в самое начало второй половины ASCII - 0x80 - 0xBF
Уф, пока все. Далее дам подробную инструкцию по конвертации TTF фонта из инета и подключения его к библиотеке
PS предупреждая возгласы "Хватит болтать, выкладывай код" :) - сорри, после замечаний товарища b8hri11 нужно еще кое-что проверить
Вот компилируемый вариант Евгения
https://github.com/b8hri11/DMDSTM
Остается еще проблема, что в Ардуино ИДЕ для кириллицы используется совсем другая кодировка, которая вроде тоже Unicode, но коды другие и даже порядок символов не тот.
На самом деле и то, и другое - юникод, только две различные его формы: utf-8 и utf-16.
utf-16 - это двухбайтовая кодировка, любой символ имеет длину ровно 16 битов. Отсюда недостаток - с utf-16 практически невозможна работа программ, рассчитанных на однобайтовыю кодировку (например, компиляторы).
utf-8 - кодировка переменной длины. Символы могут иметь различную длину - от 1 до 6 байтов. Что очень сильно затрудняет посимвольную бработку. Но зато все символы ascii представлены одним байтом. Что позволяет обрабатывать ее компиляторами, где все служебные слова и переменные задаются латинницей.
Естественно, между utf-8 и utf-16 существует взаимно однозначное соответствие и алгоримы перекодировки в обе стороны.
PS предупреждая возгласы "Хватит болтать, выкладывай код" :)
PS. Как по мне, так самое интереное на форуме как раз пишется по-русски. И в этом наблюдается явный дефицит.
Вот компилируемый вариант Евгения
https://github.com/b8hri11/DMDSTM
b8hri11 - огромное спасибо за код! Запускал и сравнивал разные варианты - но основе библиотеки, предоставленной Вами - и своей. В итоге выяснил следующее - неприятное мерцание картинки ("эффект строба") от библиотечного кода вообще не зависит - а зависит только от выбранного в скетче таймера для генерации прерывания для сканирования матриц.
Вот так запускается таймер в Ваших примерах:
Timer3.setMode(TIMER_CH1, TIMER_OUTPUTCOMPARE); Timer3.setPeriod(3000); // in microseconds Timer3.setCompare(TIMER_CH1, 1); // overflow might be small Timer3.attachInterrupt(TIMER_CH1, ScanDMD);при этом мерцания нет, картинка четкая. Но если в приведенном выше коде заменить обьект Timer3 на Timer2 - появляется строб и картинка начинает дрожать. Причину такой разницы не знаю.
В целом должен признать, что я несколько поторопился обьявлять свою библиотеку "улучшенным вариантом" библиотеки DMDSTM. На самом деле я ничего принципиально в ней не улучшил, код Евгения и так был рабочим, а только чуть его перелицевал и доофрмил - и получилась очередная библиотека-клон, коих так много на Гитхабе..
Да ладно прибедняться, лучше обьясни что не нравится компилятору под stm32duino в примере marque из dmd2. Он то ошибку в строке drawstring_p видит, то компилирует, но платка в цикличный резет уходит.
Портируется аналогично, так же таймер поменять, и ifdefine для stm добавить в паре мест. Попозже выложу на гитхаб.
выкладывайте.
А я пока фонтами займусь
Выложил на гитхаб новую версию с поддержкой Unicode шрифтов в формате Adafruit GFX.
Добавлено:
- новый класс DMD_Font с потомками DMD_Standard_Font для работы со старыми фонтами библиотеки DMD и DMD_GFX_Font для фонтов Adafruit GFX соответственно (см пример dmd_cyr_chars)
- выполнено обратное портирование на АВР, пока поддерживается только Атмега328 (см пример dmd_cyr_uno)
- добавлен метод inverseAll() для работы с матрицами. инвертированными на аппаратном уровне
- переписаны все примеры
Соответвенно изменился синтаксис работы с фонтами, метод selectFont() не совместим с прошлой версией. Документации пока нет, смотрите примеры.
DIYMan - код решил выложить полностью, так как пока нет уверенности в том, насколько он окажется полезным...
Вот DMD2 под STM, но там не работает пример marque, не понимаю что компилятору не нравится
https://github.com/b8hri11/dmd2-stm
Вот DMD2 под STM, но там не работает пример marque
А другие примеры работают? Компилятор никаких предупреждений не выдает?
А другие примеры работают? Компилятор никаких предупреждений не выдает?
С другими все в порядке, он ругается на строку drawstring_p вроде бы так, но по факту все есть. Потом бац и кломпилирует, но плата в резете
Разобрался, в примере если убрать F(""), то всё работает
Разобрался, в примере если убрать F(""), то всё работает
то есть ничего делать не надо? или нужна какая-то помощь?
Обновление - добавлена вертикальная бегущая строка. Вроде в оригинальной DMD этого нет, хотя может ошибаюсь.
Видео:
https://youtu.be/JNL-5qp6bDc
Сорри, в релиз на Гитхабе пока гне вошла.
b8hri11 - на примерах для СТМ обнаружил любопытный глюк, может вы сталкивались на своем коде или на коде Евгения.
При подключении 2х матриц анимация начинает как-то странно подтормаживать. Странно - потому что неравномерно. Две-три строчки пробегают с нормальной скоростью - потом строчки начинают ползти, зрительно раза в 2 медленнее. Немного времени пройдет - снова все бегает...
не похоже, чтобы плата не успевала - более вероятно какой-то глюк с таймером анимации.
Первую dmd не использовал нигде. Вторая в основном, но там проблем не было.
Короче чтобы избавиться от моргающих фантомных пикселей нужно закомментировать следующее, но помогает до 58% яркости)
Короче чтобы избавиться от моргающих фантомных пикселей нужно закомментировать следующее, но помогает до 58% яркости)
спасибо, попробую.
Но вообще , на Таймере3 в той версии кода, что на Гитхабе, у меня фантомные пиксели на любой яркости практически незаметны.
Дмитрий, попробовал твою библиотеку, на втором spi работает пример dmd_cyr_chars , вот два вместе spi не могу запустить, вроде все как в примере распаял, три раза уже перепроверил, спаяно норм, а не работает, в чем может быть проблема?
а в чем заключается "не работает" ? - ошибки при компиляции есть7
все компилируется, грузиться а на экране несколько светодиодов загорается и все, причем перестают работать оба ряда. сейчас сфоткаю
сейчас три светодиода загорается и все. т.е этот скетч не работает
/*-------------------------------------------------------------------------------------- double_dmd Using of two instances of DMD class on SPI(1) and SPI(2) */ /*-------------------------------------------------------------------------------------- Includes --------------------------------------------------------------------------------------*/ #include <DMD_STM32.h> //#include "fonts/SystemFont5x7.h" //#include "fonts/Arial_Black_16_ISO_8859_1.h" #include "st_fonts/UkrRusArial14.h"; #include "gfx_fonts/GlametrixLight12pt7b.h" #include "gfx_fonts/GlametrixBold9pt7b.h" // We'll use SPI_1 for first DMD and SPI_2 for second SPIClass dmd_spi(1); SPIClass dmd_spi2(2); #define DISPLAYS_ACROSS 3 #define DISPLAYS_DOWN 1 // ----- Select pins for P10 matrix connection ------------ // pins A, B, SCLK may be any digital I/O, pin nOE should be PWM pin as PB1,PA8 // SPI specific pins as CLK and R_DATA has predefined values: // for SPI(1) CLK = PA5 R_DATA = PA7 // for SPI(2) CLK = PB13 R_DATA = PB15 // -------------------------------------------------------- #define DMD_PIN_A PB11 #define DMD_PIN_B PB10 #define DMD_PIN_nOE PB1 #define DMD_PIN_SCLK PB0 //Fire up the DMD library at first as dmd DMD dmd(DMD_PIN_A, DMD_PIN_B, DMD_PIN_nOE, DMD_PIN_SCLK, DISPLAYS_ACROSS, DISPLAYS_DOWN, dmd_spi ); #define DMD2_PIN_A PB7 #define DMD2_PIN_B PB6 #define DMD2_PIN_nOE PA8 #define DMD2_PIN_SCLK PB8 // and at second as dmd2 DMD dmd2(DMD2_PIN_A, DMD2_PIN_B, DMD2_PIN_nOE, DMD2_PIN_SCLK, DISPLAYS_ACROSS, DISPLAYS_DOWN, dmd_spi2 ); // --- Define fonts ---- // DMD.h old style font DMD_Standard_Font UkrRusArial_F(UkrRusArial_14); // GFX font with sepatate parts for Latin and Cyrillic chars DMD_GFX_Font GlametrixL((uint8_t*)&GlametrixLight12pt7b,(uint8_t*)&GlametrixLight12pt8b_rus,0x80,13); /*-------------------------------------------------------------------------------------- Interrupt handler for Timer1 (TimerOne) driven DMD refresh scanning, this gets called at the period set in Timer1.initialize(); --------------------------------------------------------------------------------------*/ void ScanDMD() { dmd.scanDisplayBySPI(); dmd2.scanDisplayBySPI(); } /*-------------------------------------------------------------------------------------- setup Called by the Arduino architecture before the main loop begins --------------------------------------------------------------------------------------*/ int utf8_rus(char* dest, const unsigned char* src) { uint8_t i, j; for ( i =0, j =0; src[i]; i++) { if ((src[i] == 0xD0 )&& src[i+1]) { dest[j++] = src[++i] - 0x10;} else if ((src[i] == 0xD1 )&& src[i+1]) {dest[j++] = src[++i] + 0x30; } else dest[j++] = src[i]; } dest[j] ='\0'; return j; } void setup(void) { // Serial.begin(115200); // Serial.println("Workin...."); // initialize Timer Timer3.setMode(TIMER_CH1, TIMER_OUTPUTCOMPARE); Timer3.setPeriod(1000); // in microseconds Timer3.setCompare(TIMER_CH1, 1); // overflow might be small Timer3.attachInterrupt(TIMER_CH1, ScanDMD); //clear/init the DMD pixels held in RAM dmd.clearScreen( true ); //true is normal (all pixels off), false is negative (all pixels on) dmd2.clearScreen( true ); } /*-------------------------------------------------------------------------------------- loop Arduino architecture main loop --------------------------------------------------------------------------------------*/ void loop(void) { dmd.selectFont(&UkrRusArial_F); const char *MSG = "Привет Ардуино"; dmd.drawMarquee(MSG,strlen(MSG),(32*DISPLAYS_ACROSS)-1,0); // set brightness ( 0-65536, default is 30000) dmd.setBrightness(4000); long prev_step =millis(); dmd2.clearScreen( true ); // dmd2.selectFont(&GlametrixL); const unsigned char m[] = "Привет STM32!"; // Serial.println("DMD STM32...."); char k[30]; dmd2.selectFont(&GlametrixL); // const char *MSG2 = "Привет STM32"; utf8_rus(k,m); // dmd2.drawMarquee(MSG2,strlen(MSG2),(32*DISPLAYS_ACROSS)-1,0); dmd2.drawMarquee(k,strlen(k),(32*DISPLAYS_ACROSS)-1,0); dmd2.setBrightness(2000); while(1){ if ((millis() - prev_step) > 50 ) { dmd.stepMarquee(-1,0); dmd2.stepMarquee(-1,0); prev_step=millis(); } } }А этот работает
/*-------------------------------------------------------------------------------------- dmd_cyrillic_chars DMD_STM32 example code for STM32F103xxx board ------------------------------------------------------------------------------------- */ /*-------------------------------------------------------------------------------------- Includes --------------------------------------------------------------------------------------*/ #include <DMD_STM32.h> //#include "fonts/SystemFont5x7.h" //#include "fonts/Arial_Black_16_ISO_8859_1.h" #include "st_fonts/UkrRusArial14.h"; #include "gfx_fonts/GlametrixLight12pt7b.h" #include "gfx_fonts/GlametrixBold9pt7b.h" // We'll use SPI 2 SPIClass dmd_spi(2); //Fire up the DMD library as dmd #define DISPLAYS_ACROSS 3 #define DISPLAYS_DOWN 1 // ----- Select pins for P10 matrix connection ------------ // pins A, B, SCLK may be any digital I/O, pin nOE should be PWM pin as PB1,PA8 // SPI specific pins as CLK and R_DATA has predefined values: // for SPI(1) CLK = PA5 R_DATA = PA7 // for SPI(2) CLK = PB13 R_DATA = PB15 // -------------------------------------------------------- #define DMD_PIN_A PB7 #define DMD_PIN_B PB6 #define DMD_PIN_nOE PA8 #define DMD_PIN_SCLK PB8 DMD dmd(DMD_PIN_A, DMD_PIN_B, DMD_PIN_nOE, DMD_PIN_SCLK, DISPLAYS_ACROSS, DISPLAYS_DOWN, dmd_spi ); // --- Define fonts ---- // DMD.h old style font DMD_Standard_Font UkrRusArial_F(UkrRusArial_14); // GFX font with sepatate parts for Latin and Cyrillic chars DMD_GFX_Font GlametrixL((uint8_t*)&GlametrixLight12pt7b,(uint8_t*)&GlametrixLight12pt8b_rus,0x80,13); //DMD_GFX_Font GlametrixBold((uint8_t*)&GlametrixBold9pt7b,(uint8_t*)&GlametrixBold9pt8b_rus, 0x80, 11); /*-------------------------------------------------------------------------------------- UTF8 char recoding --------------------------------------------------------------------------------------*/ int utf8_rus(char* dest, const unsigned char* src) { uint8_t i, j; for ( i =0, j =0; src[i]; i++) { if ((src[i] == 0xD0 )&& src[i+1]) { dest[j++] = src[++i] - 0x10;} else if ((src[i] == 0xD1 )&& src[i+1]) {dest[j++] = src[++i] + 0x30; } else dest[j++] = src[i]; } dest[j] ='\0'; return j; } /*-------------------------------------------------------------------------------------- Interrupt handler for Timer1 (TimerOne) driven DMD refresh scanning, this gets called at the period set in Timer1.initialize(); --------------------------------------------------------------------------------------*/ void ScanDMD() { dmd.scanDisplayBySPI(); } /*-------------------------------------------------------------------------------------- setup Called by the Arduino architecture before the main loop begins --------------------------------------------------------------------------------------*/ void setup(void) { Serial.begin(115200); Serial.println("Workin...."); // initialize Timer3 Timer3.setMode(TIMER_CH1, TIMER_OUTPUTCOMPARE); Timer3.setPeriod(1000); // in microseconds Timer3.setCompare(TIMER_CH1, 1); // overflow might be small Timer3.attachInterrupt(TIMER_CH1, ScanDMD); //clear/init the DMD pixels held in RAM dmd.clearScreen( true ); //true is normal (all pixels off), false is negative (all pixels on) dmd.setBrightness(2000); } /*-------------------------------------------------------------------------------------- loop Arduino architecture main loop --------------------------------------------------------------------------------------*/ void loop(void) { const unsigned char m[] = "Привет Ардуино!"; Serial.println("DMD STM32...."); char k[30]; dmd.selectFont(&UkrRusArial_F); const char *MSG = "DMD STM32"; dmd.drawString(0, 0, MSG, strlen(MSG), GRAPHICS_NORMAL); delay(5000); dmd.clearScreen( true ); dmd.selectFont(&GlametrixL); utf8_rus(k,m); dmd.drawMarquee(k,strlen(k),(32*DISPLAYS_ACROSS)-1,0); long prev_step =millis(); while(1){ if ((millis() - prev_step) > 30 ) { dmd.stepMarquee(-1,0); prev_step=millis(); } } }