Игровая видеоконсоль на AVR AVGA

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

Принципы работы видео систем

Чтобы понять, как же работает игровая видеоконсоль, сперва разберемся с принципами работы аналоговой видео системы. Для этого рассмотрим пример основанный на принципе работы стандартного телевизора с электронно-лучевой трубкой (ЭЛТ).

В стандартном телевизоре с электронно-лучевой трубкой имеется экран покрытый слоем люминофора и электронная пушку, которая излучает электроны в направлении экрана. В момент удара электрона об экран и в течении небольшого времени после удара в месте удара о слой люминофора, происходит излучение света. Траекторию электронного потока, излучаемого пушкой, можно изменить с помощью магнитного поля и тогда электроны будут ударяться в другую точку экрана. Используя такое управление можно рисовать горизонтальные линии по всему экрану. При изменении интенсивности электронного луча изменяется яркость свечения, таким образом можно получить на экране изображение. В системе PAL экран перерисовывается 25 раз в секунду. Чтобы уменьшить мерцание экрана при обновлении картинки, сначала рисуются все нечетные, а потом все четные линии. Потому картинка практически обновляется 50 раз в секунду. Для того чтобы получить цветное изображение, необходимо чтобы каждая точка экрана состояла из 3-х цветов: красного, зеленого и синего.

 

Изображение на экране формируется точками разной яркости. Когда электронный луч проходит по экрану, его интенсивность изменяется за счет изменения уровня видеосигнала. Но в этом сигнале нет информации о том, в какой части экрана находится в настоящее время луч. Для решения этой проблемы используется синхроимпульс, который передается в начале каждой строки. Синхроимпульс говорит телеприемнику, что текущая строка закончилась и необходимо перевести луч в начало следующей строки. Телеприемник также должен знать, когда начинается новый кадр. Об этом сообщает специальная комбинация синхроимпульсов. При обновлении кадра 25 раз в секунду изображение будет заметно мерцать, поэтому сначала рисуются все нечетные, а потом все четные линии. За счет этого число обновлений экрана увеличивается до 50 в секунду, и изображение становится более сглаженным. Информация о четности/нечетности строки передается в комбинации вертикальных синхроимпульсов. Амплитуда видеосигнала изменяется в районе от 0 до 1В. Уровень 0,3В соответствует черному цвету, а 1В – белому (яркость цвета изменяется между этими значениями). Уровень 0В соответствует синхроимпульсу.

Для генерации видеосигнала необходима схема, способная создавать сигналы с амплитудой напряжения от 0 до 1В. Чтобы создать изображение необходимо как минимум три уровня сигнала. Телевизор должен получать уровень цвета, черного и уровень синхросигнала для того, чтобы синхронизировать изображение. Для получения 3-х необходимых уровней аналогового сигнала требуется два бита данных цифрового сигнала. Используя два резистора и два выхода порта микроконтроллера можно создать требуемые уровни напряжения.

При соединении обоих выходов 0 и 1 с землей, напряжение на видеовходе телевизора будит равно 0, что соответствует синхроуровню.

Выход 1 соединен с землей, а выход 0 — с +5В. В этом случае резистор 450 Ом включен параллельно 75 Ом-ному сопротивлению видеовхода телевизора, а резистор 900 Ом подключен к этой цепи последовательно. Этот делитель напряжения позволяет получить на видеовходе уровень 0,33В, что очень близко к уровню черного.

Выход 0 соединен с землей, а выход 1 — с +5В. В этом случае резистор 900 Ом включен параллельно 75 Ом-ному сопротивлению видеовхода телевизора, а резистор 450 Ом подключен к этой цепи последовательно. Этот делитель напряжения позволяет получить на видеовходе уровень 0,67В. Это уровень серого.

Оба выхода 0 и 1 соединены с +5В. В этом случае резисторы 900 Ом и 450 Ом включены параллельно, а 75 Ом-ное сопротивление видеовхода телевизора, подключено к этой цепи последовательно. Этот делитель напряжения позволяет получить на видеовходе уровень 1В. Это уровень белого.

Итак, мы можем создать синхроуровень, уровни черного, серого и белого. Этого достаточно для того чтобы создать простое изображение, как в играх Pong и Tetris. Возможно создание и большего числа уровней яркости если использовать большее число бит выходного порта.

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

На осциллограмме видно, что синхроимпульс имеет самый минимальный уровень сигнала, он равен 0В и его длительность составляет 4 мкс. За горизонтальным синхроимпульсом в течении 8мкс следует уровень сигнала равный 0,33В, данному уровню соответствует уровень черного, далее идет короткий импульс амплитудой 0,6В соответствующий серому, далее опять уровень черного и короткий импульс белого амплитудой 1В. Длительность всей строки составляет 52мкс. В результате подачи такого сигнала на видеовход получится такое изображение:

Отличие цветного видео от ч/б заключается в разделении белого цвета на три составляющие, это RGB цвета (R=Red – красный, G=Green – зеленый и B=Blue – синий). При смешивании данных цветов с различной яркостью можно получить любой цвет из гаммы.


Изображение показывающее эффекты наложения цветов

Реализация системы видео вывода

Разобравшись с принципом работы аналоговых видео устройств можно начать приступать к реализации собственного устройства видео вывода.

Конструктивно описываемое устройство представляет собой схему, основанную на AVR микроконтроллере, подключаемую к монитору либо телевизору через своеобразный резистивный ЦАП.

Процессоры AVR не предоставляет достаточного объема оперативной памяти для использования ОЗУ в качестве видеопамяти, поэтому некоторые элементы при работе в режиме реального времени должны использоваться в сжатом виде. Распространенный способ преодоления нехватки ОЗУ является способ разбиения экрана на блоки. В системах такого рода, экран делится на поля X*Y (блоки), и каждый блок графически адресован в таблицу текстур находящуюся в оперативной памяти. В действительности растровые изображения хранятся в другом месте, например в ПЗУ.

Основой однокристальной игровой консоли AVGA является блочная структура, основанная на прерываниях AVR управляемые аудио-видео драйвером. Но имеются некоторые очень специфичные функции:

1. Графический драйвер может получить любой растровый блок для показа в любом месте экрана из оперативной памяти или флэш. Что это значит? Вы можете скопировать блок графики из FLASH, в оперативную памяти основной программы. Для этого укажите драйверу, что он должен получить данные для этого блока из оперативной памяти, а не из флеш. На экране ничего не изменится. Тем не менее, вы можете изменить отображение, когда блок будет в оперативной памяти. Эта функция используется для наложения (Overlay). Существует функция с именем overlay_draw(pointer_to_image, x, y, width, height). Эта функция ведет себя так же, как putimage(…) в полноценной графической среде, отображая отдельные пиксель (не блок) по координатам плюс он имеет определяемую прозрачность, зеркальное отображение исходного растрового изображения, обрезка, выбор области рисования и т.д. Эта функция перемещает все блоки под данный спрайт оперативной памяти и рисует его. Тогда вы можете просто вызвать overlay_clear() и спрайт исчезает — все используемые блоки, на которые ссылаются, возьмутся из FLASH. Есть также классические примитивные графические функции, такие как putpixel(…).

2. Экран может быть разделен на вертикальные участки. Каждое такое окно что-то вроде интерфейса между видимым экраном и видео-памятью. Можно, например, создать окно где-то на экране с высотой 50px, которое показывает вещи, начиная с линии 10 в псевдо видео ОЗУ. Можно создать два окна, показывающие одно и то же (клон), создать пустое окно, поэтому эти видео линии не проверяются – это экономит процессорное время, позволяет создать окно с цветом маски, и т.д.

3. Низкоуровневый графический драйвер поддерживает горизонтальную прокрутку (один блок, пиксель за пикселем). Это позволяет писать игры с горизонтальной прокруткой. Фоновая утилита рисует блоки основного объекта на определенное место на экране (окно), расположенные относительно левого верхнего угла экрана.

4. Не имеется абсолютно никаких задержек внутри системных функций. Все прерывания ядра рассчитаны для возврата процессора в основную программу, когда это лишь становится возможным. Быстрый режим ШИМ используется для генерации высокоточных видео синхронизаций, с правильными таймингами. Это дает более чем 3М тактов процессора в секунду для основного приложения (19.6608MHz, полноэкранное видео, PAL, 192х144).

Все может быть настроено для удовлетворения ваших требований. AVGA не использует специфических особенностей МК. Он будет работать на любом AVR ATMega контроллере, по крайней мере, имеющим один полный 16-разрядный таймер для видео.

Оптимальное разрешение выбрано для PAL это 192х144 (не разогнанный МК). Это разрешение с соотношением 4:3 и делиться на восемь. Каждый кадр PAL имеет 288 строк, поэтому, когда удваивается каждая строка, она охватывает весь экран. Для NTSC это 160х120. Обратите внимание, что вы можете сделать свое собственное видео разрешение. Вы даже можете изменить высоту блока. При разгоне МК можно получить гораздо выше разрешение, например, 320×240.

Структура программы

Программа состоит из 2-х основных частей, это ядро и утилиты:

1. Ядро

a. video.c — генератор видео синхронизации сигналов. Он использует один 16-битный таймер/счетчик в режиме быстрой ШИМ без задержки цикла для высокоточной генерации сигнала синхронизации. Шаблон сигнала описан как определение (define) во флеш, при этом очень легко переключаться между видео стандартами в режиме исполнения программы. Модуль настраивается в video.h или config_hw.h и config_screen.h.

b. DRIVER.S — графический видео драйвер. Драйвер вывода видео в порт. Модуль написан на ассемблере. Он производит 4-битный видео поток пикселей с периодом цикла 5 пикселей. Ширина блока фиксируется на 8 пикселей. Разрешение и размер блока могут быть изменены. Он поддерживает один блок прокрутки вдоль горизонтальной оси. Модуль настраивается в driver.h или config_hw.h и config_screen.h.

c. sound.c — звуковой драйвер. Звуковой секвенсор. Используется 8-битный таймер/счетчик в режиме CTC для воспроизведения последовательностей аудио сигнала. Модуль настраивается в sound.h или config_hw.h.

2. Утилиты

a. window.c — утилита вертикального отображения экрана. Модуль может разделять экран для вертикальных секций. Каждый может иметь различные параметры, например: прокрутка, цвет маски, таблицы текстур и т.д. модуль настраивается в window.h или config_utils.h.

b. overlay.c — плавающий рендеринг изображений. Использование оперативной памяти драйвера для возможности отображения плавающих изображений. Это обеспечивается мощным API overlay_draw(…) который ведет себя точно так же как putimage(…) в полноценной графической окружающей среде. Он поддерживает обрезание, зеркалирование, прозрачность и т.д. модуль настраивается в overlay.h или config_utils.h.

c. background.c — поблочный рендеринга изображения. Драйвер использует PGM отображение для вывода статичных изображений. В сочетании с прокруткой, он может отображать гладкое отображение игры с возможностью вертикальной прокрутки. Модуль настраивается в background.h или config_utils.h.

Простейший пример использования функций вывода текстовой строки через драйвер видео AVGA.

#include «stdio.h»
// Подключаем файл видео драйвера AVGA
#include «AVGA/avga.h»
// Этот заголовочный файл содержит определения для проекта.
#include «tileset.h»

// Объявление глобальных переменных для видео драйвера
unsigned char screen[DRIVER_REFTABLE_SIZE];
const unsigned char pgmmap[] PROGMEM = TILESET;
unsigned char rammap[DRIVER_RAMMAP_SIZE];

int main( void )
{
//передаем карту области памяти для видео драйвера
driver_mmap(screen, pgmmap, rammap);
//инициализируемся для PAL сигнала
video_init(PAL);
//запускаем видео драйвер
enable();

//подождем секунду для синхронизации видео
wait_seconds(1);

//выводим строку
driver_print_C(6, 8, «Hello, world!»);

//будем тут до конца 🙂
while(1);
}

Требования к аппаратному обеспечению

AVGA успешно протестирована на следующих контроллерах:
ATmega16
ATMega88
ATmega168
ATMega128 (разогнан до 19.6608MHz)
ATMega644

Подключение AVR к монитору

Для своей работы система нуждается в одном 16-битном AVR таймере/счетчике. Горизонтальной сигнал синхронизации подается через выход OCRxB таймера. Выходной порт вертикальной выходной синхронизации может быть настроен в config_hw.h. Поток пикселов появляется при выставлении верхних тетрад порта. Поток пикселов может быть преобразован с помощью резисторного ЦАП в видео RGB со стандартной 16 цветной EGA/VGA палитрой.


Резистивный 4-х битный ЦАП

Где R1 = R2 = R3, R4 = R5 = R6 и R7 = R8 = R9;

Регулируя отношения R1:R4, легко меняется контраст между нижней и верхней границей цветной гаммы. Фактические значения резистора зависит от входного уровня монитора и импеданса. Вот несколько примеров:

ЭЛТ-монитор VGA и аналоговый ТВ PAL с разъемом SCART (75 входов Ом):
R1 = R2 = R3 = 470R
R4 = R5 = R6 = 680R
R7 = R8 = R9 = 100R

Sony PSOne TFT экран (высокий импеданс входа):
R1 = R2 = R3 = 2k2
R4 = R5 = R6 = 3k3
R7 = R8 = R9 = 1k


Цветовая палитра, полученная с ЦАП

Кроме того, R2R лестница может быть использована для создания 16 цветных оттенков серого. Такой сигнал может быть, например, смешан с VSYNC непосредственно для создания Ч/Б композитного видео.

Аппаратная реализация системы видеовывода AVGA

Разобравшись с принципами формирования видеосигнала можно приступать к изготовлению рабочего прототипа устройства. Основу устройства составляет микроконтроллер AVR ATMega168-20P. тактируемый от кварцевого резонатора на частоте 19.6608MHz. К 4м старшим выводам порта D микроконтроллера подключены резистивные ЦАП, формирующие необходимые уровни видеосигнала, а с вывода 2 порта В через делитель берется сигнал синхронизации SYNC. Питание схемы осуществляется через интегральный стабилизатор IC2 на микросхеме 78L05, обеспечивающий стабильное выходное напряжение 5В. Программирование устройства производится через 10 выводной ISP разъем.

Напряжение сигнала синхронизации снижается до требуемого уровня благодаря использованию резисторных делителей R11 и К12. В результате линии: R, G, B и VSYNC (HSYNC) могут быть подключены непосредственно к монитору (например, VGA D-Sub, разъем SCART, …) или к устройству преобразования сигнала RGB в композитный (например, AD725).

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

Фото собранного устройства в выводном исполнении представлено ниже:


Плата на Mega168

Программная реализация системы видеовывода AVGA

Библиотека драйвера видеовывода AVGA находится в архиве AVGA_release_022.zip. Описание API функций, для работы с библиотекой в файле AVGA_0_2_API_pre.pdf.

Фото и видео, демонстрирующее возможности библиотеки AVGA:

Пример фото и видео игры Super Mario, созданной с применением библиотеки AVGA, запущенной на плате «AVRmario»:

Пример фото и видео игры Pacman:

Примеры схемы и платы:

Версия в выводном исполнении

Описание:
Имеет CINCH разъемы для подключения аудио и каждого цвета (VSYNC лишь), стабилизатор напряжения 78L05 и шесть кнопок, извлеченных из старой AT клавиатуры.

Файлы:
m168/schematic.png   схема в формате png
m168/schematic.sch   схема в формате sch (eagle)
m168/layout.brd   файл трассировки печатной платы (eagle)
m168/config_hw.h   файл настроек конфигурации драйвера AVGA

Версия от Slime2k’s «AVRmario» в SMD исполнении

Файлы:
SuperMario/Supermario.sch   Файл схемы
Supermario/Supermario.brd   файл трассировки печатной платы (eagle)
Supermario/supermario_board.pdf   pdf файл с трассировкой печатной платы
Supermario/config_hw.h   файл настроек конфигурации драйвера AVGA

Файлы драйвера:
AVGA/AVGA_release_022.zip Драйвер системы вывода графики в аналоговом виде
AVGA/v01_demo.zip   Демо проект
AVGA/AVGA_0_2_API_pre.pdf   Справочник по API функциям AVGA

При написании статьи использовались следующие источники:

http://avga.prometheus4.com
http://www.rickard.gunee.com/projects/video/pic/howto.php
http://en.wikipedia.org/wiki/RGB_color_model
http://ru.wikipedia.org/wiki/Спрайт_(компьютерная_графика)
http://www.serasidis.gr/circuits/AVR_VGA/avr_vga.htm
http://myavr.narod.ru/video.htm

Список радиоэлементовОбозначение
Тип
Номинал
Количество
ПримечаниеМагазинМой блокнот

IC1
МК AVR 8-битATmega168P1
IC2
Линейный регуляторL78L051
D1
Выпрямительный диод1N40041
C1, C2
Конденсатор22 пФ2
C3, C6, C7
Конденсатор100 нФ3
C4, C5
Электролитический конденсатор100 мкФ2
R1
Резистор4.7 кОм1
R2-R4
Резистор2.2 кОм3
R5-R7
Резистор3.3 кОм3
R8-R12
Резистор1 кОм5
R13
Резистор22 кОм1
R14
Резистор10 кОм1
Q1
Кварц19.6608 МГц1

Разъем10 выводной ISP1
X1-X6
РазъемТюльпан "Мама"5

КнопкаТактовая6
Добавить все

Скачать список элементов (PDF)

Прикрепленные файлы:

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

Ваш адрес email не будет опубликован.