Подключение vga к stm32
Превращаем VGA-монитор в «большой OLED-экранчик» с помощью iCEstick
Работа с VGA-сигналом ранее была подробно рассмотрена в заметке Учим iCEstick передавать видео-сигнал по VGA. Кое-какие подробности об экранчиках на базе чипа SSD1306 и протоколе (точнее, протоколах) работы с ними вы найдете в посте Микроконтроллеры STM32: работа с OLED-экранчиками на базе SSD1306 по I2C и SPI. Если вдруг вы пропустили заметку Знакомимся с iCEstick и полностью открытым ПО для разработки под FPGA, ее я также рекомендовал бы к ознакомлению. Далее предполагается, что вы знакомы с материалом из этих статей.
В итоге вот что у меня получилось:
/* vim: set ai et ts=4 sw=4: */
output logic vga_r ,
output logic vga_g ,
output logic vga_b ,
output logic vga_hs ,
output logic vga_vs ,
parameter h_pulse = 96 ; // h-sunc pulse width
parameter h_bp = 48 ; // back porch pulse width
parameter h_pixels = 640 ; // number of pixels horizontally
parameter h_fp = 16 ; // front porch pulse width
parameter h_frame = h_pulse + h_bp + h_pixels + h_fp ;
parameter v_pulse = 2 ; // v-sync pulse width
parameter v_bp = 31 ; // back porch pulse width
parameter v_pixels = 480 ; // number of pixels vertically
parameter v_fp = 11 ; // front porch pulse width
parameter v_frame = v_pulse + v_bp + v_pixels + v_fp ;
logic color = 0 ;
assign vga_r = color ;
assign vga_g = color ;
assign vga_b = color ;
Рассмотрим основные части приведенного кода.
Картинка 128 x 64 хранится в регистре mem:
Запись в него происходит забавно:
Здесь используется 4-wire SPI. Но все команды к экранчику, включая команды, задающие адрес страницы, попросту игнорируются. Принимаются лишь данные, которые легко отличить от команд по высокому напряжению на пине DC. То есть, предполагается, что используемая библиотека должна передавать все страницы строго в порядке их очередности. Плюс к этому, если между передачами страниц на время пометить чип, как не выбранный, убрав низкое напряжение с пина CS, текущий адрес waddr обнулится, и фокус не удастся. Это, конечно же, грязный хак. Но он будет работать со многими библиотеками, так как страницы они передают в правильном порядке, и во время передачи не убирают высокое напряжение с CS.
Наконец, остальная часть кода просто выводит изображение по VGA. Как это делается, мы с вами уже знаем. Самым сложным было аккуратно записать формулу, вычисляющую адрес текущего пикселя в регистре mem по текущим координатам h_pos и v_pos на экране.
А вот и пример выводимой картинки:
Здесь выполняется процедура ssd1306_TestFPS() (см файл ssd1306_tests.c), которая определяет количество фреймов, передаваемых в единицу времени по используемой шине.
Итак, что же мы имеем в итоге? Повторить проект получилось, хотя завелся он и не с пол пинка. Что же до практической ценности, я думаю, что этот проект стоит держать на вооружении как минимум в обучающих целях. Кроме того, если вы когда-нибудь окажетесь недовольны устройством, использующим слишком маленький OLED-экранчик, теперь вы знаете, что его можно научить выводить изображение на монитор.
Исходники к этому посту вы найдете на GitHub. Вопросы и дополнения, как всегда, всячески приветствуются!
По роду своей работы часто приходится проектировать различные виды управляющих и измерительных систем. Разумеется на базе микроконтроллеров. Сначала использовали AVR, потом следующее семейство ATxMega, в конце концов остановились на семействе STM32. Несмотря на разные функционал проектируемых устройств, масса функций остается неизменной: интерфейс с пользователем и внешними устройствами, сохранение данных, часы реального времени и т. п. Поэтому появилась идея сделать универсальную плату контроллера содержащую основные узлы, а дополнительные подключаемые платы будут расширять функционал до необходимого. Сначала это был контроллер на STM32F103, потом на 207, потом на 429. И вот на 746 кристалле.
Но главная новинка на этой плате — это видеовыход. До этого, как правило, использовался графический дисплей (монохром, 320 х 240). Но у этого подхода есть свои недостатки:
1. При переходе на цветной дисплей приличного размера > 5" с встроенным контроллером цена становится достаточной большой.
2. Использовать приходиться только один тип дисплея, так как интерфейсы, как правило несовместимы.
Но тут пришла мысль использовать стандартные автомобильные мониторы, цена которых, достаточно демократична, существует большое количество производителей и есть разные размеры.
Для этого пришлось реализовать видео выход.
После поисков на просторах Интернета был обнаружен графический контроллер S1D13746F01, который имеет встроенную память 321 кБ и композитный видеовыход.Конечно есть и S-Video, но он не планировался использоваться. Так же был обнаружен и даташит на Evaluation Board с подробной схемой подключения, правда на корпус, который имеет 100 выводов.
После прочтения даташита на микросхему выяснилась некоторая особенность организации доступа к внутренней памяти, а именно невозможность записи по определенным адресам. То есть видеобуфер необходимо переписывать весь. Эта особенность заставляет организовать хранение видеобуфера в памяти микроконтроллера и производить его перезапись с необходимой частотой.
Для организации видеобуфера потребуется 320 * 240 = 76800 байт. Количество цветов при этом будет равным 256. Такой формат кодирования цвета обозначен в документации на видеоконтроллер, как RGB 3:3:2. То есть 3 бита на красный, 3 бита на зеленый и 2 бита на синий цвет. Итого 8 красных цветов различного уровня, 8 зеленых и 4 синих.
Запись в видеочип осуществляется программным способом через порт. Вот программка на Си.
Время записи всего видеобуфера составляет примерно 20мсек. При желании можно выводить видео 50 кадров / сек, но контроллер будет заниматься только выводом. :) В реальных задачах необходимо осуществлять перезапись экрана от 3 до 10 раз в секунду.
И вот картинка на подключенном автомобильном мониторе, купленном в ближайшем магазине. Размер монитора — 7".
Это управление высоковольтным тестирующим устройством. Кроме видеовыхода на плате контроллера остался разъем для подключения монохромного дисплея Winstar WG320240C0.
В статье Текстовый VGA-модуль для микроконтроллеров я рассказывал о доработанном варианте терминала 64х30 символов. 15 цветов. Всё хорошо, сделал плату, допилил софт под себя, но текст это текст. Только символы и только 8х16. Можно конечно рисовать псевдографикой. Таблички выходят на ура. Я даже написал колхозную функцию увеличения цифр. Работает достаточно шустро:
И что-то можно изобразить похожее на интерфейс
Но, черт возьми, как рисовать диагональные линии ? В принципе, если хорошенько дунуть пораскинуть мозгами, то можно из этой таблицы символов
выкинуть всё не нужное и заменить на своё. Как собственно это сделано с логотипом Microchip. (Символы с кодами с 0x80 по 0x98, выстроенные в 5 рядов по 5 символов, образуют графическое изображение логотипа Microchip.) Ну то есть взять и выкинуть например английский алфавит. Или русский. Или всякие стрелочки и параграфы с тильдами и стрелочками. Получаем кучу символов, которые можно перерисовать и сделать части окружностей, дуг, диагональные линии и.т.д. Потом наколбасить библиотеку, которая будет рисовать кривые этой псевдографикой. Но что-то это сильно пахнет адским геморроем и я начал искать варианты с графикой.
// отображение пикселя
case WORK_PUT_PIXEL:
break;
В итоге функция рисования линии по двум точкам стала одной функцией а не тремя. (Хотя я понимаю, это было для быстродействия). Мой код в этом нисколько не проиграл. Функцию рисования окружностей сделал по алгоритму Брезенхэма. У Этого дядьки окружности более округлые. Переписал все команды по-своему, а их там 31. Сделал рисование не только белым, но и черным. Задаваемую толщину линий. Закраску прямоугольников и окружностей. Встроил второй громадный шрифт. Запилил пропорциональное увеличение шрифта 8*16 на заданную величину множителя. Убил весь обратный вывод в UART. Не нужно ничего отвечать на команды. Просто рисуй быстрее. Одно подтверждение обработки команды тормозит вывод графики в десятки раз. Естественно добавил кольцевой буфер на 1024 байта. Конечно UART в этом смысле достаточно убогий по скорости интерфейс, но зато более универсальный. Во-первых, в микроконтроллере их может быть несколько, а во-вторых, можно и в ПК воткнуть. I2C интерфейс убил по-этой же причине. Да и в скорости он не сильно выигрывает, особенно софтварный.
Далее пошли первые тесты. (фотографировал на тапок)
Округлые окружности
Прямоугольнички
А дальше пошел писать библиотеку для ардуино. В этом плане всё проще. Берем стандартный класс SoftwareSerial и наследуемся от него, добавляя свои функции.
Можно конечно юзать и 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);
Собственно всё рисование сводится к последовательной подаче байт в UART.
void VgaSoftwareSerial::pixel(int x1, int y1, uint8_t color)
<
uint8_t buf[] = 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);
>
А так рисуется линия:
В самом скетче всё еще проще:
И на обоих мониторах рисуется точка по центру.
Обкатанные платы были заново отрисованы в DipTrace и отправлены на завод.
Изменилась схема питания. Применен прекраснейший DC-DC преобразователь LM3671MF. Платы стали еще меньше.
И это, на минуточку, PIC32. Расту над собой 🙂
Такими же нехитрыми командами был наколбашен тест всего что есть в модуле.
В дальнейшем использовал сей девайс для отрисовки приборной панели для второй версии автомобильчика.
Ну и 3D до кучи:
Данный VGA-модуль доступен в 4х вариантах исполнения и вы можете его приобрести в соответствующем разделе.
Буду рад вопросам и комментариям. Еще больше буду рад рационализаторским предложениям.
Совсем недавно мы познакомились с работой FSMC в STM32, подключили дисплей и раскрасили его разными цветами (вот). И пока еще не забыли все, что обсуждали в прошлой статье, давайте выведем на дисплей, что-нибудь поинтереснее, а именно какую-нибудь картинку.
Что же нам для этого понадобится?
Ну во-первых сама картинка. Находим какое-нибудь изображение размером 320*240. Я секунд за 20 нагуглил подходящую картинку — кадр из фильма Железный Человек, реклама которого, кстати, последние пару недель находится абсолютно везде что в интернете, что на улице 🙂
Итак, мы подготовили то, что надо загрузить в память дисплея. Необходимо как-то преобразовать готовую картинку в массив чисел, соответствующих каждому отдельному пикселю. Но для начала конвертируем наш файл в формат bmp, это нам понадобится для дальнейших махинаций. Для того, чтобы получить необходимые данные из bmp файла воспользуемся отличнейшей утилитой от Segger под названием Segger Bitmap Converter. Дружно скачиваем ее, устанавливаем и запускаем.
Давайте разбираться как с помощью этой программы конвертировать изображение в RGB формат (назовем его так). А довольно-таки просто. Заходим в меню File->Open и первым делом загрузим нашу картинку. Кстати, ее необходимо еще повернуть на 90 градусов, чтобы она легла на дисплей как надо. Получаем вот что:
Появляется еще одно окно и вот тут то уже мы можем выбрать требуемый формат. Выбираем High Color (565), red and blue swapped и, нажав ОК, получаем готовый .с файл с сохраненным массивом данных для нашего изображения.
Далее я последовал следующий путем. В проект из предыдущей статьи (ссылка была в начале) я добавил еще один файл fsmc.h, в котором объявил массив и просто скопировал в него значения, которые выдала утилита Segger Bitmap Converter. Получилось вот что:
Теперь еще пара небольших правок в функции main():
И вот, в принципе, все готово! Мучительно ожидаем, пока программа наконец-то зальется в микроконтроллер, и на дисплее появится картинка!
Вот таким образом мы сумели залить в память дисплея обычное изображение. В дальнейшем думаю еще что-нибудь замутим с STM32 и дисплеем, что-то мне понравилось это дело 🙂
Читайте также: