VGA: Generujeme obraz

Víte, co v mém případě rozhodlo, že se naučím pracovat s FPGA? Byla to právě snadnost generování videosignálu. Tam, kde se u jednočipů a procesorů neobejdete bez specializovaných obvodů nebo velmi přesného časování, najednou nejste s FPGA ničím omezeni! Pojďte se přesvědčit!

Nespočtukrát jsem po večerech snil o vlastním osmibitu s videovýstupem. Asi bych to dokázal nějak udělat pro černobílý PAL signál, ale to není ono. Barevný signál bych asi zvládl taky, i když bych si k němu musel dát nějaké ty AD7xx, co převedou RGB na PAL. Proti tomuto řešení hovořily dva argumenty. Zaprvé: Je s tím spousta práce a piplačky. Zadruhé: Barevných televizí je čím dál míň, zato VGA monitory se všude válejí už skoro „za odvoz“. Takže VGA. Jenže vytvořit signál pro VGA monitor není až taková brnkačka. U AVR s jeho taktem okolo 20 MHz jste už dost na hraně. Párkrát jsem přemýšlel, jak by to asi bylo složité v případě FPGA, a říkal jsem si, že asi hodně. Ale pak jsem viděl tohle video.

FPGA totiž často obsahují PLL, které umožňují vygenerovat velmi širokou škálu hodinových pulsů. U FPGA máte třeba 48MHz krystal, a díky PLL z toho vygenerujete klidně 51,84MHz (frekvence pro VGA rozlišení 768 x 576). Nebo i 162 MHz (1600×1200). Nebo i jiná. Nejste totiž vázáni rychlostí procesoru, ale spíš mezními hodnotami FPGA, a ty jsou dostatečně vysoko. Navíc, jak jsme si už říkali, je FPGA „masivně paralelní“, takže to není tak, že bychom v jednom cyklu museli přečíst data, převést je na RGB, spočítat synchronizaci, ale tohle všechno se děje najednou.

Teorie: VGA signál

Videosignál se skládá ze snímků (u prokládaného videa je to komplikovanější o půlsnímky). Každý snímek začíná nějakou synchronizací („Teď jsme na horním okraji obrazovky“), pak je chvíle klidu (zatmění, „back porch“), pak se vykresluje obraz po jednotlivých řádcích, pak je zase zatmění („front porch“), pak synchronizace, back porch, obraz… a tak dále. Tohle celé se děje alespoň 50x za sekundu, u VGA monitorů častěji (60, 72, 75, 85, 100). Je to známá obrazová obnovovací frekvence a udává se v hertzech.

Snímek se kromě synchronizace a zatmění skládá z obrazových řádků. Možná vám teď bude připadat, že se opakuju, ale: obrazový řádek u VGA začíná synchronizačním pulsem, pak je chvíle klidu (back porch), pak jednotlivé pixely tak jak jsou vedle sebe zleva doprava, pak zase zatmění (front porch), a následuje sync…

Máme tedy osm základních časovacích údajů, čtyři pro horizontální a čtyři pro vertikální. Vždy to je: front porch, sync, back porch a video. U řádků (horizontálně) se udává v jednotkách „pixelů“ (např. „sync puls trvá 120 pixelů“), u snímků (vertikální jednotky) se udává v počtu řádků (např. „vertikální synchronizace trvá 6 řádků“). Někde se udávají tyto počty v mikrosekundách a milisekundách, zejména u videosignálu pro PAL (kde jeden řádek trvá 64 mikrosekund), ale u VGA je praktičtější udávat je tak, jak jsem napsal, tedy v pixelech a řádcích.

Další důležitý údaj je „pixelová frekvence“. Tedy rychlost, jakou je potřeba posílat pixely do monitoru. Tato rychlost je definovaná ve standardech pro jednotlivá rozlišení. Například rozlišení 800 x 600 na 72 Hz používá pixel clock rovno 50 MHz. Tedy na jeden pixel máme 1/50M = 20ns. To je naše základní jednotka. Jeden řádek tedy zabere 800 pixelů (800 * 20 = 16 µs) plus neviditelnou část: 56 pixelů front porch, 120 pixelů horizontální synchronizace, 64 pixelů back porch. Tedy 1040 pulsů hlavních hodin = 20,8µs.

Totéž platí i pro vertikální rozlišení. 600 řádků obrazu + 37 řádků front porch + 6 řádků synchronizace + 23 řádků back porch  = 666 řádků. Jestliže každý řádek trvá 20,8µs, tak vynásobením dostáváme trvání jednoho snímku 13,8528ms, což dává opravdu obnovovací frekvenci 72 Hz (přesně 72,187572 periodicky).

Synchronizace

Na jednu stranu jsou monitory poměrně tolerantní a odpustí vám drobné (ale jen opravdu drobné) rozladění. Dokážou se i přesto „chytit“. Na jiné signály jsou zase docela citlivé, a vám se tak stane, že koukáte na známé NO SIGNAL. Proto je dobré snažit se dodržet časování co nejlíp to půjde.

Už jsme si řekli, že u řádku i u snímku je vždy oblast aktivních dat a oblast zatmění a synchronizace. Dřív, u CRT monitorů, se zatmění používalo k návratu elektronového paprsku zpátky na levý či horní okraj obrazovky. Každopádně: Po posledním pixelu na řádku stáhněte výstupy R, G, B na nulu. Během front porche se jen čeká. Při horizontální synchronizaci se posílá puls na vodič HSYNC. Na konci se HSYNC vrací do klidu a opět se čeká (back porch). To, jestli je HSYNC normálně 1 a puls 0, nebo jestli je normálně 0 a sync puls 1, je opět určeno standardem (Hsync positive / Hsync negative).

Totéž platí pro vertikální synchronizaci.

Pojďme si teď ukázat entitu sync, která z hodinového cyklu vygeneruje potřebné synchronizační pulsy (hsync, vsync a blank):

Použil jsem časování pro režim 640×480 na 85 Hz. Horizontální konstanty jsou: 640 pixelů video, 32 front porch, 48 sync, 112 back porch, Hsync negativní. Vertikální konstanty: 480 řádků video, 1 řádek front porch, 3 řádky sync, 25 řádků back porch, Vsync negativní.

V kódu jsou dva čítače, hpos a vpos. Hpos čítá postupně front, sync, back a video, Vpos ve stejném pořadí. Z toho vyplývají i různé „magické konstanty“ v kódu: hpos 32 znamená „konec front porch“, hpos 80 je „front porch + sync“, hpos 192 je celé horizontální zatmění. U vertikálního je to obdobné.

R, G, B

Vlastní videosignál je u VGA analogový. Pro takové to domácí použití si vystačíme s prostými několikabitovými odporovými převodníky, pro profesionální zařízení pak použijte specializované rychlé D-A převodníky. Já jsem se chystal spájet takový převodník pro svůj oblíbený kit, ale nakonec jsem získal velmi levně jiný kit, který už má VGA zapojené „z výroby“. Používá šestnáctibitové rozhraní, zapojené jako 5-6-5. Tedy 5 bitů pro červenou, 6 bitů pro zelenou, 5 bitů pro modrou. A jak je vidět ze schématu, moc se s tím nemazali:

Clipboard01

PLL

Pro generování obrazu ještě potřebujeme ten známý „pixelový kmitočet“. K jeho vygenerování slouží právě PLL. Použití PLL se liší u jednotlivých výrobců FPGA. Já se podržím toho, co nabízí Altera.

Nejprve vyberte z menu Tools možnost „MegaWizard Plug-In manager“. Nechte „vytvořit novou megafunkci“ a v dalším kroku vyberte „altpll“ (je v oddíle I/O). Jako jazyk vyberte VHDL, vyberte vhodné umístění a jméno souboru („pll“ není špatné) a pokračujte dál v průvodci.

Na straně 3 vyberte rychlost vašeho FPGA (udává to číslice na konci označení, já mám EP4CE6E22C8N – a rychlost je právě těch 8). Zadejte vstupní frekvenci v MHz (na mém kitu to je 48), pokračujte dál. V kroku 4 vypněte všechny funkce (reset, locked output apod.), aby bylo PLL co nejjednodušší.

pll1

Klikejte klidně na next, next, next, až se dostanete do oddílu 3 – Output Clocks (stránka 8). Zde máte dvě možnosti: Buď zadáte násobitel a dělitel (celá čísla), nebo požadovanou hodnotu výstupní frekvence (dělitel a násobitel se dopočítají z té zadané vstupní). Pokud budu implementovat rozlišení 640×480@85Hz, budu potřebovat frekvenci 36 MHz, kterou získám ze vstupních 48 prostým vynásobením zlomkem 3/4. Získám i další frekvence, např. 25/24 mi dá 50 MHz, 27/8 zase 162 MHz (1600×1200@60Hz).

pll2

Další hodinové výstupy nebudeme potřebovat, nepotřebujeme ani specifický fázový posun nebo duty cycle, takže můžeme klidně použít Finish. Wizard vygeneruje několik souborů a vloží je do projektu (popř. se zeptá, jestli to má udělat).

Kalkulačka!

Jestli se vám motá hlava z nadmíry konstant a frekvencí, tak nezoufejte! Připravil jsem pro vás kalkulátor frekvencí pro VGA, PLL a vůbec všechno to, co jsme si teď říkali. Stačí zadat frekvenci krystalu, kterou máte k dispozici, a kliknout na Go!

V tabulce se objeví přehledně všechna VGA rozlišení, která lze z této hodinové frekvence získat. K nim všechny potřebné konstanty a dokonce i nastavení PLL (násobitel a dělitel). Kalkulačka vždy počítá frekvence tak, aby násobitel a dělitel byla celá čísla. Jejich velikost si můžete omezit v poli PLL max div (např. pro jednodušší PLL).

Když si vyberete vhodné rozlišení a kliknete na něj, tak vám kalkulačka vygeneruje vylepšenou komponentu sync.vhd (viz výše) a ukáže aktuální hodnoty průběhu signálů.

Při svých testech jsem se odvážně pouštěl dál a dál, a nakonec jsem použil rozlišení 1920×1080@60Hz, tedy Full HD. Pixelová frekvence je 148,5MHz, PLL koeficient 99/32 (a opravdu, 48 * 99 / 32 = 148,5). S trochou rozechvění jsem nahrával kód do FPGA, a po několika sekundách se na monitoru objevilo:

IMG_20150605_193443

IMG_20150605_193448

 

Jak se říká: Bylo to tam!

Jednoduchý obrazec

Synchronizace je nezbytný základ, je to kostra, na který se navěsí vlastní zobrazování. Vylepšená komponenta sync posílá kromě synchronizačních pulsů a signálu blank i dvě čísla, totiž posx a posy, neboli pozici horizontálně a pozici vertikálně. Funkce je následující: Když je zatmění (blank), tyto čísla ignorujte a na výstupy R,G,B posílejte 0. Pokud není zatmění, posílejte na výstupy barvu pixelu na dané souřadnici.

Komponentu PLL1 mi vygeneroval výše zmíněný MegaWizard. Komponentu sync jsem si nechal vygenerovat svojí kalkulačkou. A zbytek už je jen „když je pozice taková a onaká, tak nastav barvu na …“ Vlastně taková obdoba „hello world“ pro VGA.

A příště budeme pokračovat, teď se totiž otevřelo obrovské pole působnosti…

PS: Šlo by použít místo VGA třeba HDMI? Šlo. Bylo by samosebou zapotřebí upravit výstup, protože HDMI používá digitální sériový přenos údajů a diferenciální budiče, ale základní princip zůstane stejný, a díky paralelní podstatě fungování FPGA nás převod hodnot na HDMI nijak „nezpomalí“.