USB монитор из Futaba GP1160A02A

730ada2c7bb1de4c16e639e4a69f0052.jpg

Когда-то вакуумно-люминесцентные индикаторы создавались как альтернатива газоразрядным, а также в целях загрузить производство электроламповых заводов, так как в те времена лампы уже начали терять актуальность и на их место пришли полупроводники.

Прожив долгую 60-летнюю историю, ВЛИ уходят в небытие — оставшиеся в живых производители уже сообщили о том, что жизненный цикл этих приборов окончен и пора переходить на новые технологии.

Но ни один современный дисплей не способен передать это завораживающее бирюзовое сияние возбужденного люминофора, находящегося под воздействием электронной бомбардировки. 

Про один из таких дисплеев, изготовленных по этой технологии и будет рассказано в этой статье, а именно про модуль Futaba GP1160A02A.

Эта статья не будет про подключение этого дисплея к какой-нибудь «ардуине» стандартными способами. Это слишком просто и скучно. Речь пойдет про обход ограничений, наложенных производителем, и попытки выжать из этого дисплея всё, что позволяет технология.

Посмотрим, что представляет из себя и что может этот дисплейный модуль «из коробки».

59e23d54906fcb9231825ea7f660225f.jpg

На плате модуле распологается графический вакуумно-люминесцентный индикатор (далее ВЛИ) разрешением 256×64 пикселей, достаточно крупный по размерам — отображаемая область 163.69×38.25 мм. Общий размер модуля 220×60 мм, вес 240 гр.

Модуль управляется по RS-232 при помощи ESC-последовательностей. Может работать как в графическом, так и в текстовом режимах. Имеет встроенные шрифты разных размеров и начертаний, поддерживает загрузку пользовательских символов. Питается от 5 Вольт, так как на плате распаян преобразователь, который формирует все напряжения, необходимые для работы ВЛИ.

И казалось бы, что всё замечательно, но у этого модуля есть фатальный недостаток — максимальная скорость передачи данных по RS-232 составляет всего 38400 бит/с.

6e8be90589dc47cf4a414a0f00745d96.png

Это означает, что если мы заходим максимально быстро обновлять картинку на дисплее, то максимальный теоретический фреймрейт составит всего 2.5 кадра в секунду. А в реальности еще меньше, так как контроллеру требуется время на то, чтобы обработать принятые данные.

Конечно, для отображения статической графики и текста этого более чем достаточно. Но если захотеть крутить видео на этом дисплее или отображать графику с несколькими оттенками цвета, то этого явно маловато будет. И хотелось бы каким-нибудь образом обойти это ограничение.

Я сразу отверг идею написать прошивку для установленного на плате модуля микроконтроллера NEC d70f3214gc, так как не знаком с этими контроллерами. А изучать это семейство ради одного проекта не очень хочется. 

Поэтому наиболее логично будет попробовать порулить ВЛИ напрямую, при помощи какого-нибудь AVR или STM32 микроконтроллера. 

Определение распиновки ВЛИ

У установленного на этом модуле ВЛИ нет торчащих наружу выводов сеток и анодов. Вместо этого внутри ВЛИ есть чип, который выполняет всю низкоуровневую работу (технология называется chip-in-glass, сокращенно CIG).

e4262b64a5f3ace098313db11367d15e.png

Задача состоит в том, чтобы разобраться с распиновкой дисплея, понять в каком виде данные посылаются в чип, отрезать родной микроконтроллер модуля от ВЛИ, подключить свой и написать для него программу. 

Вся сложность заключается в том, что на момент, когда я делал всё, что написано в этой статье, у меня не было в наличии даташита на чип дисплея. Поэтому всё пришлось делать буквально методом тыка при помощи осциллографа и логического анализатора.

Управляется и питается дисплей через две группы контактов — 16 слева и 15 справа (я не беру в расчет самые крайние выводы, так как понятно, что это выводы нитей накала).

Первым делом необходимо определить какие контакты дисплея отвечают за питание, а какие за управление и какие на них уровни сигнала.

Вооружившись осциллографом, смотрим уровни сигналов на выводах дисплея:

278613f8147774c70213e161ebe49172.png

Если внимательно посмотреть на плату модуля, то можно увидеть, что некоторые линии между двумя группами контактов соединены. 

Теперь, вооружившись мультиметром, прозваниваем линии между собой. За одно ещё прозваниваем все линии к земле и к питанию +5 В на предмет подтяжек, а также прозваниваем линии между дисплеем и микроконтроллером. Получаем следующее:

3b16588175104ab769744b9de2419147.png

Итого всего для управления чипом дисплея задействовано 8 уникальных линий, которые подключены к микроконтроллеру модуля через 100-омные резисторы.

Так как уровни управляющих сигналов 5 В, то теперь можно смело подцепить логический анализатор. Видим следующую картину:

343613afdaf54b7e9f78d34ca58c59b5.png

Первые три линии представляют собой нечто очень похожее на SPI шину: канал 0 — Линия данных, канал 1 — Линия тактирования, канал 2 — Выбор ведомого (chip select).

Пробуем очистить весь дисплей:

d90d1dc90c93e5e7b1ba77180f4ad800.png

Линия Data ушла вниз. Теперь попробуем заполнить весь дисплей:

f4d9f5c240a2fe3f723e578fbf3cf85f.png

Теперь линия Data ушла вверх. Причём между этими прыжками вверх/вниз ни шине больше ничего нет. Поэтому можно смело предположить, что чип получает графические данные в сыром виде, без каких бы то ни было управляющих команд. Также это подтверждает тот факт, что при подаче питания на модуль в дисплей сразу грузятся пакеты с графическими данными.

Путём зажигания отдельных пикселей, строк и столбцов определяем соответствие битов пакета данных, полученного по SPI, с тем что отображается на дисплее.

Спойлер

Прям буквально — зажигаем, к примеру, левый верхний пиксель и смотрим какой бит в пакете принял состояние »1». Проделываем это с тремя остальными углами. Потом зажигаем строки, потом столбы и так далее. Процесс чем-то напоминает разгадывание нонограмм (японских головоломок), только в обратном порядке.

Следующие две линии — включение выходов анодных регистров, а именно сигналы BLK1 и BLK2. Эти сигналы практически в противофазе, причем не допускается, чтобы оба сигнала были низкого уровня (об этом ниже):

586218b2e9f22b24cf13e59b6aa7f0cf.png

И последние три линии — это входы сдвигового регистра сеток, я могу сказать это основываясь на своём личном опыте работы с подобными дисплеями. Единственный нюанс — в этом регистре постоянно находятся два бита (т.е. постоянно включены две сетки):

fa33cc898298d10de8032c5b3e086b69.png

Таким образом, получаем следующую распиновку дисплея:

ebe1dd534a644079826b5fb446e1d6fd.png

Краткое описание работы CIG

Чип дисплея содержит четыре сдвиговых регистра.

Два 128-битных — отвечают за аноды (сегменты). Два 64-битных — отвечают за сетки.

Правда, если посмотреть на структурную схему чипа в даташите, то можно заметить, что сеточных регистров 4 штуки (DRV1, DRV3, DRV4, DRV6). Но к одной группе сеток параллельно подключено два регистра, которые принимают на вход (и соответственно выдают) одинаковые сигналы.

9f7e5612b47fb77e74c4b11189ce45aa.png

Анодные регистры на плате модулясоединены параллельно (линии CLK, DATA и СS). Один регистр отвечает за отображение сегментов в столбцах DA, другой в столбцах BC. Включение выходов нужного регистра осуществляется подачей низкого уровня на вход BLK1 или BLK2.

ВНИМАНИЕ! Одновременная подача на входы BLK сигналов низкого уровня не допускается — дисплей может выйти из строя.

Каждый 128-битный пакет содержит данные для отображения двух столбцов пикселей. Заполнение столбцов идет слева-направо снизу-вверх. Старший бит пакета соответствует левому нижнему пикселю, младший правому верхнему.

5eba1ca3cb0f4620a47aaf62e70bb251.png

Регистры управляются по шине SPI, режим работы: CPOL=1, СPHA=1. Частота тактирования: в оригинале 1.6 МГц, максимально допустимая по даташиту 5 МГц. Я же запускал с частотой 10.5 МГц и всё прекрасно работало. Единственное условие для работы на такой частоте — чипу дисплея требуются уровни 5 В, поэтому для микроконтроллеров STM32 нужно использовать преобразователь (я использую 74HCT245).

Сеточные регистры соединяются последовательно. В принципе, можно считать, что это один 128-битный регистр, так как на плате модуля выход первого регистра соединён со входом второго и больше никуда не подключен.

Особенность конструкции дисплея в том, что хоть в нём и 128 сеток (одна сетка расположена на двумя столбцами пикселей), но для правильного отображения информации и для избежания засветок/подсветок одновременно должны быть включены две соседние сетки.

Включение выходов сеточного регистра осуществляется подачей низкого уровня на вход GBLK. Через него же осуществляется управление яркостью дисплея.

ВНИМАНИЕ! Ни при каких обстоятельствах не оставляйте вывод GBLK свободно висящим или постоянно подтянутым к низкому уровню — дисплей может выйти из строя.

Сканирование (развёртка) идёт по столбцам слева-направо. Первый пакет содержит данные для второго столбца пикселей (сегменты BC сеток G1 и G2).

Анимация, упрощённо показывающая работу развертки:

12be22bf38aa0d81c18549de736c87a0.gif

ВНИМАНИЕ! Ни при каких обстоятельствах не оставляйте развертку при включенном питании — дисплей может выйти из строя.

К чему я пишу все эти предупреждения

Tсли что-то пойдет не так, замешкаться хотя бы на пару секунд и не отключить питание от дисплея, то на люминофоре можно получить вот такие неприятные выжженные пятна:

4993286c7d78d1aba23f96be32e3c64b.jpg

А если ещё немного замешкаться, то сетка и люминофор скорее всего прогорят до дыр. У меня была ситуация, когда развертка остановилась, а питание было подано. И в том месте, где остановилась развертка, через несколько секунд сетка раскалилась до красна. После этого дисплей практически перестал работать — при включении было еле-еле заметное свечение люминофора. Я уже было списал этот дисплей в утиль. Но к моему удивлению, через несколько дней дисплей ожил, правда немного потерял в яркости.

Поэтому любые подключения/отключения разъемов и программирование микроконтроллера должны быть при отключенном питании дисплея 5 В. Включение дисплея должно быть в последовательности: сначала подаём питание на МК, потом, через пару секунд на дисплей. Выключение в обратном порядке. И нужно быть готовым к тому, что что-то может пойти не так и питание дисплея нужно будет быстро отключить.

Так же я крайне не рекомендую играться с мощными магнитами с этими дисплеями. В виду того, что нити накала длинные, сетки длинные, а расстояние между нитями и сетками маленькое, при поднесении магнита сетки может просто выгнуть и замкнуть на нити.

Подключение к МК

В первую очередь необходимо отключить распаянный на плате модуля микроконтроллер от дисплея. Выпаивать его для этого не нужно, достаточно лишь снять две резисторные сборки под позиционными номерами RA3 и RA4:

b990bc22424377aadfb5ad2eb02e2a83.jpg

Теперь нужно каким-нибудь образом подключиться к контактам дисплея. Сначала я делал это при помощи крючков от логического анализатора, но это крайне ненадёжный способ, так как эти дурацкие крючки очень любят слетать в самый неподходящий момент. Чтобы не мучиться, я напрямую припаял к контактам дисплея разъем типа L-KLS1-XL1–2.00–10-R (MW-10MR). Получилось вполне себе прочно и надёжно. По крайней мере, за полтора года разъем не отвалился:

5ebdf5205b8a164b9d80b12ee18a0643.jpg

Проводок МГТФ идущий от 5 пина разъема (если считать справа налево) — линия BLK2. Чтобы не тянуть провод через всю плату модуля ко второй группе контактов, я припаял его к ближайшему переходному отверстию этой линии:

00058cb6af120b7a77931e2076528824.jpg

Осталось самое простое — написать программу для МК. 

Сначала нужно просто проверить, будет ли оно работать вообще. Поэтому для первых тестов я буду просто выводить какой-нибудь простенький графический паттерн, например «шашечку». Также я пока не буду заморачиваться с точным соответствием таймингов, главное соблюсти всю последовательность действий и обеспечить сколько-нибудь адекватную скорость развертки.

Последовательность действий следующая:

  • Загружаем два бита в регитстр сеток

  • Загружаем 16 байт в регистр анодов. Так как мне нужна «шашечка», то это просто 16 одинаковых байт 0×99

  • Включаем на некоторое время регистр сеток и второй регистр анодов (BLK2)

  • Выключаем все регистры

  • Сдвигаем данные в регистре сеток на один бит

  • Опять загружаем 16 байт 0×99 в регистр анодов

  • Включаем на некоторое время регистр сеток и первый регистр анодов (BLK1)

И так 128 раз.

Как ни странно, всё заработало буквально с первого раза:

5a71177514788ac3416f4e8a72199c97.jpg

Но отображать статическую графику дисплей мог и из коробки. Попробую отобразить какую-нибудь анимацию. Графические данные буду передавать с ПК, на нём же буду делать все преобразования из картинки в готовый массив, который можно сразу лить в анодные регистры дисплея:

slslbfdd2db25657f9a04199a5adf92d49b0.gif

Ну, а так как дисплей теперь способен отображать анимацию с (относительно) высоким FPS, то значит на нем можно запустить «Bad apple!»:

Про формирование изображения с несколькими оттенками цвета на монохромных дисплеях я писал в статье про ИМГ-1–02. Здесь используется тот же самый принцип. На дисплее отображается 8 оттенков цвета, поэтому частота развертки дисплея должна быть в 7 раз выше исходной. Т.е. для того чтобы глаз увидел 8 оттенков при 60 кадрах в сек. частота развёртки должна быть 420 Гц.

Кроме того это требует стабильной передачи данных с ПК со скоростью почти 3 Мбит/с (256×64x3×60 = 2949120 бит/с), а в режиме с двумя дисплеями 6 Мбит/с. Большинство USB-UART адаптеров уже не могут работать с такой скоростью, поэтому передача данных с ПК в МК теперь осуществляется посредством USB.

На микроконтроллере реализовано полноценное WinUSB WCID устройство (это не эрзац USB CDC VCP), которое не требует драйверов. Работоспособность проверена в Windows 7/10 и Linux Mint 20.3. 

Со стороны ПК используется библиотека libusb-1.0, а со стороны МК USB-стек libopencm3. Для передачи графических данных с ПК в МК используется одна конечная точка типа Bulk. Регулировка яркости дисплея осуществляется при помощи vendor-specific реквеста в нулевую конечную точку (bmRequestType = 0×41).

Dual Mode

Это вариант исполнения, при котором к микроконтроллеру одновременно подключено два дисплея. 

Сначала я пытался подключить оба дисплея к одной шине SPI и по очереди загружать в них данные, выбирая нужный дисплей при помощи chip select (все остальные линии при этом были запараллелены). 

Но то ли я что-то не так делал, то ли не укладывался в тайминги, то ли дисплеи некорректно реагировали на уровень сигнала на линии CS — адекватной работоспособности мне так и не удалось получить. Либо дисплеи вообще ничего не отображали, либо отображали мусор. Хотя по записям с логанализатора всё было в полном порядке.

В итоге я сделал проще — один дисплей подключил к шине SPI1, а второй к шине SPI2. И так как в микроконтроллере STM32F401CCU6 эти шины привязаны к разным контроллерам DMA, то у меня появилась возможность загружать данные не по очереди, а параллельно, со скоростью 10.5 МГц.

907a4101d916979af29ab4492ca3c175.png

Теперь можно поиграть в Doom 2:

Понятно, что практической пользы от такого режима нет, но выглядит это очень необычно. Особенно мне нравится некое подобие эффекта параллакса, возникающего из-за смещения дисплеев относительно друг друга.

Монитор ресурсов

В качестве демонстрационного проекта я сделал монитор ресурсов компьютера, который отображает загрузку/температуру/частоту CPU/GPU, использование RAM/VRAM, и тд. и тп.

Так как в Python под Windows нет нормальных библиотек для мониторинга ресурсов, то для получения информации с датчиков я использую стороннее ПО, а именно MSI Afterburner. Особенность этой утилиты в том, что она выдаёт информацию со всех датчиков в область памяти под названием Shared Memory. Данные хранятся в очень удобном формате в виде структур. 

Первые 32 байта — структура заголовка со следующими полями:

struct MAHM_SHARED_MEMORY_HEADER {
    unsigned int signature;              // Сигнатура "MHAM". Именно так наоборот.
    unsigned int version;                // Версия ПО
    unsigned int headerSize;             // Размер структуры заголовка в байтах
    unsigned int entryCount;             // Количество структур датчиков
    unsigned int entrySize;              // Размер одной структуры датчика в байтах
    int time;                            // Время/дата в формате unix time stamp
    unsigned int gpuCount;               // Количество структур GPU
    unsigned int gpuEntrySize;           // Размер одной структуры GPU в байтах
};

Далее идут структуры, описывающие каждый датчик (Entry):

struct MAHM_SHARED_MEMORY_ENTRY {
    char name[260];                      // Имя датчика
    char units[260];                     // Единиа измерения датчика
    char localName[260];                 // Локализованное имя датчика (имя датчика на выбраном языке интенрфейса MSI AB)
    char localUnits[260];                // Локализованная единица измерения датчика
    char format[260];                    // Формат (в большинстве случаев %.0f)
    float data;                          // Значение датчика
    float minLimit;                      // Минимальное значение датчика
    float maxLimit;                      // Максимальное значение датчика
    unsigned int flags;                  // Флаги (1 - датчик отображается в OSD, 2 - датчик отбражается на Logitech LCD, 4 - датчик отобраается в системном трее)
    unsigned int gpu;                    // Номер GPU, к которму относится датчик
    unsigned int srcid;                  // ID GPU, к которму относится датчик
};

И в конце располагаются структуры, описывающие каждый GPU (если их несколько):

struct MAHM_SHARED_MEMORY_GPU_ENTRY {
    char gpuId[260];                     // Имя GPU в формате PCI\Ven_xxxx&Dev_xxxx
    char gpuFamily[260];                 // Модель чипа GPU (например TU106)
    char gpuDevice[260];                 // Модель видеокарты (например NVIDIA GeForce RTX 2060 Super)
    char gpuDriver[260];                 // Версия видеодрайвера
    char gpuBIOS[260];                   // Версия BIOS GPU
    unsigned int gpuMemAmount;           // Объем памяти GPU в Кб
};

Огромный плюс в том, что размер полей структур фиксированный, поэтому можно один раз прочитать заголовок и далее считывать показания с датчиков просто по смещению.

GUI реализован на прекрасном в плане возможностей и скорости работы, но ужасном в плане документации dearpygui. Есть возможность отображения двух графиков с любых датчиков, а также четырех датчиков в формате текст/горизонтальная гистограмма.

3dc8d83386eaeaecb3452fc87d851bd1.png

Графика отрисовывается при помощи OpenCV. Но так как в этой библиотеке функции отрисовки текста поддерживают только лютые ШГ под названием HERSHEY, то пришлось написать простенький знакогенератор с поддержкой шрифтов 5×7, 8×12 (олдскульный шрифт из MS-DOS 6, с полной поддержкой кодовой страницы CP1251) и 14×28 (я уже не помню какой шрифт брал за основу). Шрифты 5×7 и 8×12 поддерживают кириллицу, правда в тектовых полях GUI она может отображаться некорректно.

Результат работы программы выглядит так:

9c0c79d056bd63cf57aadc9a2bcd0fc7.jpg

При желании количество и расположение графиков и сенсоров можно менять. Если вы, конечно, разберётесь в моём коде.

Заключение

Конечно, можно сказать, что практического смысла в трансляции области экрана на такой дисплей не много. Но следует понимать, что это всего лишь демонстрация технических возможностей, а не какой-то прикладной проект.

Все подобные дисплеи (VFD с CIG) работают по одному и тому же принципу, разница лишь в разрешении, в количестве сеток и в количестве групп сегментов. 

Поэтому я надеюсь, что описанная в этой статье процедура определения распиновки, принципа работы и запуска такого дисплея в нестандартном режиме поможет кому-то реализовать свои интересные проекты не только с таким же дисплеем, но и со многими подобными.

Благодарю за внимание. С радостью отвечу в комментариях на все ваши вопросы, мысли, идеи и предложения.

Ссылки

Репозиторий проекта:

https://github.com/iiiytn1k/Futaba_GP1160A

Даташит на модуль Futaba GP1160A02A:

https://drive.google.com/file/d/1QNiJgS1HvPm1EsE7q9DTeu8UQaYPqzj6/view? usp=sharing

Даташит на дисплей Futaba GP1160AI:

https://drive.google.com/file/d/1sOqzbvRTYMqEifbONbMpwJkFPG4Bj00U

© Habrahabr.ru