Графический VGA-модуль для микроконтроллеров

В статье Текстовый VGA-модуль для микроконтроллеров я рассказывал о доработанном варианте терминала 64х30 символов. 15 цветов. Всё хорошо, сделал плату, допилил софт под себя, но текст это текст. Только символы и только 8х16. Можно конечно рисовать псевдографикой. Таблички выходят на ура. Я даже написал колхозную функцию увеличения цифр. Работает достаточно шустро:

И что-то можно изобразить похожее на интерфейс

 (данное фото сделано на тапок)

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

выкинуть всё не нужное и заменить на своё. Как собственно это сделано с логотипом Microchip. (Символы с кодами с 0x80 по 0x98, выстроенные в 5 рядов по 5 символов, образуют графическое изображение логотипа Microchip.) Ну то есть взять и выкинуть например английский алфавит. Или русский. Или всякие стрелочки и параграфы с тильдами и стрелочками. Получаем кучу символов, которые можно перерисовать и сделать части окружностей, дуг, диагональные линии и.т.д. Потом наколбасить библиотеку, которая будет рисовать кривые этой псевдографикой. Но что-то это сильно пахнет адским геморроем и я начал искать варианты с графикой.

Конечно, выводить на VGA-монитор информацию средствами микроконтроллера — это жесть. Те, кто знает что для этого нужно делать и с какой скоростью, меня поймут. А тем, кто не знает, 2 варианта: 1) читать принцип формирования vga-сигналов 2) поверить на слово, что борьба идет за каждый такт.

Поиск дал неутешительные результаты. Есть разработки 800*600, но опять только текстовый режим. Есть графический цветной, но 128*96 (Черт возьми, представил себе 19″ монитор с картинкой такого разрешения). Есть какой-то мутный проект на ATmega644P якобы 640*480 256 цветов, что при 20МГц атмеге просто не реально. К этому видео отключены комментарии. Есть проект 40*30 символов и 320*240 на dsPIC33E. Кроме как побаловаться и поизобретать Dendy лично я никаких перспектив не вижу. Есть на STM32F103 проект 256×192 с 64 цветами. Тоже сега мега драйв… и еще один на этом же камне 400*200 черно-зеленый. Есть проекты на микроконтроллере STM32F4. Выход: 800х600 60 Гц, разрешение 400х300. Очень жирное железо для такой задачи, и то не справляется. И туева хуча проектов аля «тестер монитора». Это можно соорудить и на pic12f629, тупо закрасив весь экран в один цвет. Толку от такой «видеокарты» мало. Почему-то все эти проекты делают упор на сохранение многоцветности и/или использование двойной буферизации. Отсюда и расход памяти, отсюда и низкое разрешение. Конечно быстрее протолкнуть 400*300 чем 800*600. Аж в 4 раза быстрее 🙂 Нет, есть проекты даже с буфером в SDRAM и какими-то (даже углубляться не стал) микрухами с аппаратным формированием сигналов vga, но сложность этих схем, громоздкость и стоимость перечеркивают всё на корню. Кому нахрен нужен модуль вывода графики c микроконтроллера на монитор через HDMI в FullHD и 16М цветов за 158$ ? Микроконтроллер же не успеет ничего формировать. Ну сколько у вас займет чтение с SD-карты фотографии 2-3Мб на 8-и битной ардуине и проталкивание этого добра в модуль? А именно такие объемы и захочется. Иначе зачем полноцветный 1080p?

В этом плане RaspberryPi Zero и дешевле и функциональнее. Как же так ? Неужели невозможно по-простому сделать выход 640*480 с разрешением 640*480 ? Просто сделать дисплей 12864 так, чтобы он был 640480 🙂 Для ч/б варианта это 38,4кБ памяти. (видимая область). Вообще ни о чем по нынешним меркам. То есть diy-мейкеры довольствуются дисплейчиками 1602, 2004, 12864 и всякими 0.96″ OLED. Нет, ну я тоже их использую, когда это необходимо. Но только когда это необходимо и достаточно.

Но это всё «синяя изолента». О проекте контроллера для сауны напишу позднее.

Вот в нем планировалось использование этих дисплеев. А как же развитие ? А как же стремление вперед ? То есть крайности какие-то. Или быстро, дешево и убого 2″, или медленно и дорого, но в полном разрешении и цветности. 21-ый век на дворе, а с ардуины мы можем максимум куда вывести изображение — это на модуль 320*240, который 5″ LCD, задействуя кучу ног. О быстродействии не буду судить, ибо даже не пытался. Причем это как минимум должна быть arduino mega на Atmega2560. Для нее шилд и делался. Младшие довольствуются вышеупомянутыми сине-белыми штуками. Есть Nextion HMI, но опять же цена. Проще купить самый дешевый планшет и забыть про микроконтроллеры, погрузившись в программирование под Андройд.

Отвлекся я сильно. Вижу что утомил 🙂 Ну так вот… Тот же самый Игорь прислал мне «в подарок» на ДР схему графического терминала 640*480 одноцветный. И код к ней!. Как знал что я мучаюсь…

Уж не знаю, его это разработка или нет, не суть, все равно большое человеческое спасибо за идею. На тот момент для меня vga конечно был темный лес, но код-то на СИ! Ух я сейчас развернусь!… и развернулся. Как обычно, прога была не доделана, но, как известно, дареному коню в зубы не смотрят 🙂 Такое чувство, что в порыве стремления к прекрасному люди просто в какой-то момент перегорают идеей и бросают. Резко и навсегда. Тут надо помнить про Леонардо да Винчи, который 12 лет губы Моны Лизы рисовал. Потому-что часто отвлекали: то сделай им водокачку, то кран подъёмный, то летать хотим… 🙂 Это наверное про теорию «80/20». Когда 80% работы делается за 20% времени. Остальные 20% работы делается за 80% времени. Но уже лень столько времени тратить ради мизерного результата по сравнению с уже сделанным. За собой тоже такое замечал, но нашел по-моему гениальный выход из этого адского круга: всеми своими проектами я занимаюсь по спирали. Перегорел одним — пошел развивать другой. Потом вернулся. Ну, например, свежая мысль пришла.

Итак, опять схема без платы… Геморрой… (то же мне, любитель всего готовенького) Ну ничего 🙂 По-быстрому наваял рисунок, сделал плату, как обычно, в духе адского минимализма.

А заодно, раз уж управление VGA-модулем подразумевается через UART, развел на плате FT232RL чтобы можно было прямо из программы-терминала посылать команды в виртуальный COM-порт. Мозг уже рисовал в воображении, что можно ведь несколько таких модулей подключить по USB и выводить на несколько мониторов информацию из своей программы на том же C#. Благо команды однобайтные и никакого труда не составит воплотить клиента на любом языке программирования.

Как-то вот так оно и подключалось: с одной стороны USB, с другой, блин… Монитор! 🙂

Сам удивляюсь как смог всё разместить на плате таких размеров. И тут понеслось… Во-первых код написан как псевдомногозадачная система. Отдельная задача на UART, отдельная задача на изменение буфера, и.т.д. А сам вывод в монитор через DMA. 2 таймера для синхронизаций и весь массив буфера строк непрерывно валится в SPI. Любое фоновое изменение массива моментально отображается на экране. Это гениально. Но мог бы и сам догадаться… наверное… сильно позже. Во-вторых в какой-то момент произошел взрыв срыв. Я удалил всё что было в коде, оставив только инициализацию переменных и DMA. А всё остальное написал заново. Ну не могу я в чужом коде копаться, особенно когда он не дописан. Точнее заточен именно на вывод текста а не графики. Текст меня как-то мало интересовал уже.

// отображение пикселя
case WORK_PUT_PIXEL:
break;

Ну как так-то ? 🙂 Это же самое главное 🙂 А если учесть что 640 это больше чем 1 байт, то для передачи координат одного пикселя нужно использовать 2 байта на X и 2 байта на Y… И писал я долго и упорно…

В итоге функция рисования линии по двум точкам стала одной функцией а не тремя. (Хотя я понимаю, это было для быстродействия). Мой код в этом нисколько не проиграл. Функцию рисования окружностей сделал по алгоритму Брезенхэма. У Этого дядьки окружности более округлые. Переписал все команды по-своему, а их там 31. Сделал рисование не только белым, но и черным. Задаваемую толщину линий. Закраску прямоугольников и окружностей. Встроил второй громадный шрифт. Запилил пропорциональное увеличение шрифта 8*16 на заданную величину множителя. Убил весь обратный вывод в UART. Не нужно ничего отвечать на команды. Просто рисуй быстрее. Одно подтверждение обработки команды тормозит вывод графики в десятки раз. Естественно добавил кольцевой буфер на 1024 байта. Конечно UART в этом смысле достаточно убогий по скорости интерфейс, но зато более универсальный. Во-первых, в микроконтроллере их может быть несколько, а во-вторых, можно и в ПК воткнуть. I2C интерфейс убил по-этой же причине. Да и в скорости он не сильно выигрывает, особенно софтварный.

Далее пошли первые тесты. (фотографировал на тапок)

Округлые окружности

Прямоугольнички

А дальше пошел писать библиотеку для ардуино. В этом плане всё проще. Берем стандартный класс SoftwareSerial и наследуемся от него, добавляя свои функции.

#include "VgaSoftwareSerial.h"
VgaSoftwareSerial::VgaSoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false): SoftwareSerial(receivePin, transmitPin, inverse_logic = false) {}

Можно конечно юзать и HardwareSerial, но их в ардуине гораздо меньше чем SoftwareSerial 🙂 Cофтовый UART спокойно работает на 115200 и в arduino nano (Atmega328) и в arduino pro micro (Atmega32u).

class VgaSoftwareSerial : public SoftwareSerial {
public:
VgaSoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);

void cls(void);
void pixel(int x1, int y1, uint8_t color);
void setcolor(uint8_t color);
void setcursor(uint8_t x1, uint8_t y1);
void line(int x1, int y1, int x2, int y2, uint8_t color);
void rectangle(int x1, int y1, int x2, int y2, uint8_t color, uint8_t filling);
void circle(int x1, int y1, uint8_t color, uint8_t radius, uint8_t filling);
int version(void);

private:
};

Собственно всё рисование сводится к последовательной подаче байт в UART.

Пиксель:

void VgaSoftwareSerial::pixel(int x1, int y1, uint8_t color)
{
    uint8_t buf[] = {0x11, // команда установки X1 Lo
                       x1, // значение X1 Lo
                     0x10, // команда установки X1 Hi
                  x1 >> 8, // значение X1 Hi
                     0x13, // команда установки Y1 Lo
                       y1, // значение Y1 Lo
                     0x12, // команда установки Y1 Hi
                  y1 >> 8, // значение Y1 Hi
                     0x03, // команда установки цвета пикселя
                    color, // значение цвета пикселя
                     0x0A  // команда рисования пикселя
                    }; 
    VgaSoftwareSerial::write(buf, 11);
}

А так рисуется линия:

void VgaSoftwareSerial::line(int x1, int y1, int x2, int y2, uint8_t color)
{
uint8_t buf[] = {0x11, x1, 0x10, x1 >> 8, 0x13, y1, 0x12, y1 >> 8, 0x15, x2, 0x14, x2 >> 8, 0x17, y2, 0x16, y2 >> 8, 0x03, color, 0x0B};
VgaSoftwareSerial::write(buf, 19);
}

В самом скетче всё еще проще:

#include "VgaSoftwareSerial.h"
VgaSoftwareSerial MonitorOne(8, 9);
VgaSoftwareSerial MonitorTwo(10, 11);
MonitorOne.pixel(320, 240, 1);
MonitorTwo.pixel(320, 240, 1);

И на обоих мониторах рисуется точка по центру.

Обкатанные платы были заново отрисованы в DipTrace и отправлены на завод.

Изменилась схема питания. Применен прекраснейший DC-DC преобразователь LM3671MF. Платы стали еще меньше.

И это, на минуточку, PIC32. Расту над собой 🙂

Такими же нехитрыми командами был наколбашен тест всего что есть в модуле.

В дальнейшем использовал сей девайс для отрисовки приборной панели для второй версии автомобильчика.

Ну и 3D до кучи:

Данный VGA-модуль доступен в 4х вариантах исполнения и вы можете его приобрести в соответствующем разделе.

Буду рад вопросам и комментариям. Еще больше буду рад рационализаторским предложениям.

Об авторе demid

Однажды открыл для себя микроконтроллеры и с тех пор не отпускает...
Запись опубликована в рубрике VGA-модуль. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

code