Видеокарта из металлолома и синей изоленты

Задумывались ли вы над тем, каким образом компьютер отрисовывает изображение на мониторе? Как по одному проводу каждую секунду передаётся сотня миллионов пикселей? В этом видео автор рассказывает, как работает монитор с VGA интерфейсом, и попутно собирает примитивную видеокарту на базовых цифровых микросхемах: логических элементах, триггерах и счётчиках.

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

Во второй части автор заканчивает сборку схемы и показывает, как видеокарта отрисовывает картинку.

Немного теории

0:12 Распиновка VGA интерфейса.

0:47 Коротко о том, как монитор отрисовывает изображение.

1:42 На этой диаграмме видно, в какие именно промежутки времени нужно посылать сигналы горизонтальной и вертикальной синхронизации.

Что такое тайминги монитора и от чего они зависят

2:44 Нужно узнать конкретные значения периода синхроимпульсов. По запросу «VGA signal timing» можно легко найти таблицы, в которых приводится зависимость разрешения монитора от таймингов синхроимпульсов. Тайминги — определённые временные участки синхроимпульсов либо задержки между ними. На сайте приведены все популярные разрешения мониторов с соответствующими таймингами. Конечно, даже самые примитивные видеокарты поддерживают несколько разрешений, но наша задача — сделать хоть какую-то.

3:41 У каждого разрешения своя частота отрисовки пикселей. Для конкретной частоты нужен кварцевый резонатор той же частоты. Если кварца выбранной частоты нет, можно замедлить частоту отрисовки пикселей, отсылая несколько пикселей за раз. Тогда с кварцем в 12 МГц можно будет работать с монитором, у которого пиксельная частота, например, 36 МГц. Но в отличие от частоты, тайминги должны оставаться неизменными.

Тайминги зависят как от частоты кварца, так и от разрешения монитора. Возьмём разрешение 800×600. У этого разрешения пиксельная частота равна 40 МГц. Чтобы запустить схему с частотой в 10 МГц, нужно компенсировать частоту количеством отрисовываемых пикселей. Поэтому схема должна работать с разрешением 200×600. Условно за один такт будет отправляться 4 пикселя, поэтому монитор должен определить исходное разрешение как 800×600.

Горизонтальная синхронизация

Счётчик горизонтальной синхронизации

5:49 Начать стоит со схемы, которая будет считать количество пикселей. Схеме нужно понимать, где будет 0-й пиксель, где — 50-й, а где — 200-й (конец отрисовки строки). В данном случае схема должна считать от 0 до 264 (учитывая синхроимпульс). После этого нужен будет второй счётчик, считающий количество строк и, соответственно, выдающий уже вертикальный синхроимпульс, который будет означать начало отрисовки нового кадра.

В схеме будет использоваться двоичный четырёхразрядный счётчик 74LS161. Чтобы досчитать до 264, потребуется 9 бит. Почему именно 9? Чтобы определить максимальное число, до которого досчитает счётчик, нужно возвести двойку в степень его разрядности. 2^8 = 256 — мало, 2^9 = 512 — достаточно, 2^10 = 1024 — излишне много. Так как у одного счётчика на борту всего 4 разряда, то потребуется три микросхемы.

6:28 Сначала у счётчиков нужно объединить контакты питания и «земли». Обнуляться счётчики должны одновременно, поэтому стоит объединить и контакты сброса. Так как три счётчика должны работать как единый, то и тактироваться они должны синхронно. Поэтому контакты тактирования всех трёх счётчиков нужно объединить. У счётчиков есть контакты входа данных, но они не потребуются.

7:05 Чтобы счётчики работали, нужно подать высокий уровень сигнала на разрешающий контакт PE. Так как контакты входа данных не используются, то и контакт Load будет бесполезен. Но стоит обратить внимание на то, что у него отрицательный активный уровень сигнала. Поэтому, чтобы этот контакт не активировался, на него тоже нужно подать высокий уровень сигнала.

7:17 У счётчиков есть второй разрешающий контакт — TE. У первого счётчика его нужно подключить к высокому уровню — счётчик будет увеличиваться при каждом такте. Но остальные два не всегда должны считать — они должны инкрементироваться только при переполнении предыдущего счётчика. Сигнал переполнения счётчика можно снять с контакта Ripple-Carry. Значение второго счётчика будет увеличиваться на единицу при каждом переполнении первого. Поэтому нужно подключить контакт Ripple-Carry первого счётчика к разрешающему контакту TE второго счётчика, а Ripple-Carry второго — к TE третьего.

После всех этих манипуляций получится 12-разрядный счётчик (хоть нужны всего первые 9 разрядов).

7:54 Чтобы посмотреть на работу схемы, можно подключить к выходу каждого счётчика светодиод (желательно через токоограничивающий резистор). Если сейчас подключить питание, то ничего не произойдёт, потому что у схемы нет источника тактирования. Если подключить кварцевый генератор на 10 МГц, то светодиоды будут переключаться так быстро, что будет казаться, что они просто беспрерывно горят. Лучше тактировать схему от генератора.

8:54 Демонстрация работы счётчика на частоте 1 КГц. Но даже на такой частоте переключения светодиодов достаточно быстрые, поэтому можно понизить частоту до 100 Гц.

9:22 Работа счётчика на 10 Гц.

Распознавание тактов

10:00 Следующая задача — заставить схему реагировать тогда, когда значение счётчика (т. е. число на выходе) будет равняться 200, 210, 242, 264. После последнего значения счётчик должен обнуляться. Для начала число 200. В бинарном коде оно выглядит как 011001000. Чтобы определить это число, нужно поставить инверторы на все выходы счётчика, на которых должен быть ноль, а выходы этих инверторов вместе с остальными контактами подключить к 8-входовому элементу И-НЕ. В таком случае на выходе логического элемента ноль будет только тогда, когда с выхода счётчика будет поступать число 200.

Но у этого элемента 8 входов, а выходов со счётчика 9. Решить эту проблему можно не подключая старший бит — он равняется 0 (схема не будет успевать досчитывать до того момента, когда этот бит станет 1).

11:27 Таким же образом нужно будет определить все остальные числа, просто подключая инверторы к нужным битам. Но остаётся число 264. У него в 9 разряде есть единица (в отличие от всех остальных). В этом случае вместо первого бита нужно будет подключить последний.

Схема декодирования тактов

12:25 Чтобы определить все числа, вам не нужно по 8 инверторов на каждое из них — достаточно инвертировать каждый бит по отдельности, и соответственно каждому числу параллельно подсоединить входы И-НЕ элементов (на инвертированные и неинвертированные биты соответственно).

12:50 Инвертировать сигналы будет микросхема 74LS04, которая содержит 6 отдельных инверторов. Этих микросхем понадобится две. Нужно подключить все выходы счётчика к входам инверторов. Не стоит забывать и про питание микросхемы.

13:15 Теперь пришла очередь И-НЕ элементов. Для них будет использоваться микросхема 74LS30, у которой как раз 8 входов. Для начала число 264 — оно будет сбрасывать счётчик. Стоит помнить, что для этого числа первый бит не подключается. Дальше всё просто: где должен быть 0 — подключаете бит к входу логического элемента через инвертор, где 1 — напрямую.

14:50 Чтобы проверить схему, к выходу И-НЕ элемента можно подключить светодиод. У такого элемента отрицательный активный сигнал. Если подключить генератор и отправить 264 такта, то после последнего светодиод потухнет — это и будет сбросом счётчика. Теперь схема сможет автоматически обнулять счётчик при достижении числа 264.

15:43 То же самое теперь нужно сделать с оставшимися 3 И-НЕ элементами и числами 242, 210 и 200. В отличие от числа 264, у этих должен быть подключён первый бит, а отключён — последний.

17:40 Чтобы проверить работу 4 элементов, на вход счётчика нужно последовательно послать сначала 200 тактов, потом ещё 10, 32 и 22. С такой комбинацией светодиоды, подключённые ко выходам элементов, должны последовательно затухать на один такт.

Но после 264-го такта счётчик всё ещё не обнуляется — для этого нужно выход соответствующего логического элемента подсоединить к контакту сброса одного из счётчиков. Схема циклична.

Разрешающие сигналы отрисовки и синхроимпульсов

18:36 Теперь задача стоит в том, чтобы дать схеме понять, когда именно отображать пиксели (между 0-м и 200-м тактом), а когда давать синхроимпульс (между 210-м и 242-м тактом). Для этого будет использоваться RS-триггер с инверсными входами (потому что у выхода логического элемента активный низкий уровень).

19:15 На вход триггера SET нужно подавать сигнал после 264-го такта (за такт до отображения пикселей), а при достижении 200-го такта — сбрасывать триггер (подавать сигнал на вход RESET). В таком случае пиксели нужно отрисовывать только тогда, когда триггер будет установлен. Второй RS-триггер будет подавать синхроимпульс (между 210-м и 242-м тактом). Так как у синхроимпульса активный низкий уровень, то сигнал нужно снимать с инверсного выхода триггера.

19:47 Чтобы сделать два таких RS-триггера, потребуется 4 элемента 2И-НЕ — LS7400. Подключаем питание и перекрёстно соединяем выходы И-НЕ с их входами (согласно схеме). Первый триггер нужно устанавливать при достижении 264-го такта, поэтому подключаем его к выходу соответствующего логического элемента (того самого, который сбрасывает счётчик). Аналогично подключаем сброс триггера к элементу, соответствующему числу 200.

20:33 Если подключить к выходу триггера светодиод, то можно увидеть, как он горит в промежутке от 0-го до 200-го такта и не горит в промежутке от 200-го до 264-го такта.

Таким же способом нужно сделать и второй триггер. Устанавливаться он будет от элемента 210-го такта, а сбрасываться — от 242-го. Если подключить светодиод к выходу второго триггера, то можно увидеть, как он загорается в некотором промежутке, пока не горит первый светодиод.

21:08 Теперь схема знает, в какие именно моменты нужно отображать пиксели, а в какие — давать горизонтальный синхроимпульс. Но задача стоит в том, чтобы попасть в тайминги, приведённые в таблице. Для начала нужно подключить кварцевый генератор к входу тактирования счётчика. Теперь отрисовывать одну строку схема будет всего 26,4 мкс. Чтобы замерить такой незначительный интервал, потребуется осциллограф.

22:20 Замер таймингов счётчика.

Вертикальная синхронизация

Счётчик вертикальной синхронизации

24:11 Если все временные интервалы совпадают, то горизонтальная синхронизация работает как нужно. Теперь соберём схему вертикальной синхронизации. Её работа почти идентична горизонтальной, за исключением того, что схема будет считать не пиксели, а линии. При каждом обнулении счётчика горизонтальной развёртки, счётчик вертикальной будет увеличиваться на единицу. Так же, как и в первом случае, схема должна различать интервалы между 0-й и 600-й строкой, чтобы отображать строки, и интервалы между 601-й и 605-й, чтобы давать вертикальный синхроимпульс. После 628-й строки счётчик должен обнулиться.

Декодирование тактов

25:03 Чтобы определить нужные такты, потребуются всё те же элементы 8И-НЕ. Но есть проблема: у элементов всего 8 входов, а чтобы передать числа нужных тактов, потребуется 10 бит. Но 8-й и 9-й биты можно не подключать. Ведь если 8-й бит будет единицей, то число будет уже 728, а схема не успеет досчитать до него — счётчик сбросится уже на 628-м такте. По этой же причине 9-й бит тоже может быть не подключён.

26:07 Из-за особенностей чисел тут потребуется всего 5 инверторов.

26:33 Создавать второй счётчик будем так же, как первый. Разница лишь в том, что у второго будут использоваться 10 бит вместо 9. Эти 10 бит нужно подать на соответствующие входы инверторов.

26:57 Как и в прошлом случае, те разряды, которые должны быть равны 1, нужно подключить напрямую к входам логического элемента, а те, которые должны быть равны 0 — через инвертор. Выход элемента, соответствующего числу 628, нужно соединить со сбросом счётчика.

Разрешающие сигналы отрисовки и синхроимпульсов

27:30 Для этой схемы тоже понадобится 2 RS-триггера. Первый будет разрешать отображение строк, второй — подавать вертикальный синхроимпульс. Вход SET первого триггера нужно подключить к сигналу сброса счётчика, а вход RESET к выходу 600-го такта. Вход SET второго триггера нужно подключить к выходу элемента 601-го такта, а RESET — к выходу 605-го.

28:06 Тактировать второй счётчик от генератора не нужно. Вход тактирования этого счётчика нужно подсоединить к контакту сброса первого счётчика. Теперь первый считает количество пикселей (горизонтальная синхронизация), а второй — количество строк (вертикальная синхронизация).

Проверка схемы

28:55 Проверка таймингов вертикальной и горизонтальной синхронизации.

Частота вертикальной синхронизации намного меньше, чем у горизонтальной. Это стоит учесть при настройке осциллографа.

30:50 Если все тайминги соответствуют таблице, то эту схему уже можно будет подключить к монитору через VGA интерфейс. Сейчас нужны будут только три контакта: горизонтальная синхронизация (13), вертикальная синхронизация (14) и общий. Все общие контакты разъёма нужно объединить в один. Если схема собрана правильно, то монитор автоматически настроится на разрешение 800×600.

***

Для желающих собрать: полную схему со списком элементов можно посмотреть на этом сайте.

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Honor Cup, бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации