Obsah
1. Operace s framebufferem na Raspberry Pi
2. Čipy s architekturou VideoCore
3. BCM2835 a BCM2836 použité v projektu Raspberry Pi
4. Přístup k framebufferu na nejnižší úrovni (mailboxy a přímý přístup do videopaměti)
6. První demonstrační příklad: přečtení základních informací o framebufferu
8. Třetí demonstrační příklad: přístup do framebufferu s využitím mmap
9. Repositář s demonstračními příklady
10. Funkce použité v demonstračních příkladech
1. Operace s framebufferem na Raspberry Pi
Jednodeskový mikropočítač Raspberry Pi se v současnosti používá v mnoha projektech, ať již se jedná o jeho využití při výuce, implementaci různých řídicích systémů, multimediálních center, jednoúčelových serverů či o levný desktop nebo webový kiosek. Jedním z cílů, který si tvůrci tohoto projektu vytkli, bylo navržení takového mikropočítače, jehož princip fungování bude tak jednoduchý, že i relativní začátečníci nebudou mít problémy s pochopením, co a proč se interně v jednotlivých integrovaných obvodech, z nichž se Raspberry Pi skládá, odehrává (ostatně právě i z tohoto důvodu se Raspberry Pi často, a to nikoli neprávem, srovnává se slavným ZX Spectrem). Tento cíl byl – až na jednu výjimku zmíněnou v dalším textu – splněn; Raspberry Pi například nemá ani vlastní BIOS, bootovací proces je poměrně přesně popsán, stejně jako způsob ovládání jednotlivých pinů (GPIO) apod.
Mnoho projektů či výukových seriálů, v nichž se Raspberry Pi používá, se zaměřuje hlavně na použití GPIO. Na tom samozřejmě není nic špatného, ovšem nesmíme zapomenout na to, že ten stejný čip, který obsahuje jádro ARM, zajišťuje bootování, poskytuje vývojářům všechny funkce dostupné přes GPIO (SPI, PWM atd.), obsahuje i další neméně užitečnou část. Tou je video řadič spojený s programovatelným grafickým akcelerátorem. V dnešním článku si nejprve popíšeme, jak lze (prozatím čistě teoreticky) ovládat takzvaný framebuffer přímým přístupem k řídicím registrům tohoto video řadiče a přímým přístupem do paměti framebufferu. Posléze se zaměříme na použití rozhraní k video řadiči poskytovaného linuxovým kernelem. Ten totiž umožňuje aplikacím ovládat video řadič přes speciální zařízení /dev/fb0. Díky tomu je možné na Raspberry Pi vytvářet aplikace s grafickým výstupem bez nutnosti spouštění systému X Window, správců oken, desktopových prostředí a dalších mnohdy zbytečných vrstev mezi uživatelskou aplikací a hardwarem (aplikací s přímým přístupem k framebufferu existuje velké množství, například prohlížeč obrázků fbi, multimediální přehrávač MPlayer, webové prohlížeče links2 a NetSurf, mnohé hry založené na knihovně SDL atd.).
Poznámka: pro přístup k framebufferu lze použít i knihovnu DirectFB nebo (nepřímo) SDL, ale dnes se zaměříme skutečně na tu nejjednodušší možnost, tj. na použití zařízení /dev/fb0 z céčkového programu. Mezi našimi programy a hardwarem tak bude stát jen jediná mezivrstva.
2. Čipy s architekturou VideoCore
To, že mikropočítače Raspberry Pi mají s ohledem na jejich cenu a energetické nároky velmi dobré multimediální schopnosti, je zajištěno použitím čipu založeného na architektuře grafických (spíše však obecně multimediálních) procesorů pojmenovaných VideoCore. Tato architektura byla vyvinuta firmou Alphamosaic a současně je vlastněna společností Broadcom, což je ostatně jeden z důvodů, proč tuto architekturu nenalezneme u dalších výrobců čipů s jádrem ARM (ovšem v této oblasti existuje velká konkurence, takže se setkáme i s dalšími podobnými GPU, například od firem TI, NVidia či Freescale). Architektura VideoCore je plně programovatelná, což je poměrně důležité, protože je pro ni možné vytvářet nové kodeky, používat kodeky, které nemusí být licencovány, využít VideoCore i v těch aplikacích, které nepotřebují vysoký multimediální výkon, ale například provádí mnoho operací s maticemi atd. Právě poslední možnost, tj. použití VideoCore pro urychlení „běžných“ aplikací, otevírá nové možnosti i pro mikropočítače typu Raspberry Pi (SoC použitý v Raspberry Pi je navržen tak, že hlavní procesor by měl mít na starost pouze řízení GPU, což je splněno při jeho použití v multimediálních centrech apod.).
V současnosti existují minimálně čtyři generace grafických procesorů založených na VideoCore. Tyto procesory jsou většinou umístěny na jednom čipu s hlavním procesorem a řadičem periferních zařízení, takže vzniká SoC (System on a Chip), což je ostatně i případ mikropočítače Raspberry Pi. Podívejme se na některé čipy obsahující VideoCore a popř. i hlavní procesor a další pomocné obvody:
# | Čip | GPU | CPU |
---|---|---|---|
1 | VC01 | VideoCore 1 | × |
2 | BCM2702 (VC02) | VideoCore 2 | × |
3 | BCM2705 (VC05) | VideoCore 2 | × |
4 | BCM2091 | VideoCore 4 | ? |
5 | BCM2722 | VideoCore 2 | × |
6 | BCM2724 | VideoCore 2 | × |
7 | BCM2727 | VideoCore 3 | × |
8 | BCM11181 | VideoCore 3 | × |
9 | BCM2763 | VideoCore 4 | × |
10 | BCM2820 | VideoCore 4 | ARM1176 |
11 | BCM2835 | VideoCore 4 | ARM1176 (standardně 700 MHz) |
12 | BCM2836 | VideoCore 4 | Quad-core Cortex-A7 (standardně 900 MHz) |
13 | BCM11182 | VideoCore 4 | × |
14 | BCM11311 | VideoCore 4 | Dual-core Cortex-A9 |
15 | BCM21654 | VideoCore 4 | Cortex-A9 + Cortex-R4 |
16 | BCM21654G | VideoCore 4 | Cortex-A9 (až do 1 GHz) |
17 | BCM21663 | VideoCore 4 | Cortex-A9 (až do 1.2 GHz) |
18 | BCM21664 | VideoCore 4 | Cortex-A9 (až do 1 GHz) |
19 | BCM21664T | VideoCore 4 | Cortex-A9 (až do 1.2 GHz) |
20 | BCM28150 | VideoCore 4 | Dual-core Cortex-A9 |
21 | BCM21553 | VideoCore 4 | ARM11 |
22 | BCM28145/28155 | VideoCore 4 | Dual-core Cortex-A9 (až do 1.2 GHz) |
23 | BCM23550 | VideoCore 4 | Quad-core Cortex-A7 (až do 1.2 GHz) |
3. BCM2835 a BCM2836 použité v projektu Raspberry Pi
Z tabulky vypsané v předchozí kapitole nás nyní budou zajímat především řádky číslo 11 a 12, protože v mikropočítačích Raspberry Pi nalezneme buď čip BMC2835 nebo BMC2836:
Mikropočítač | SoC |
---|---|
Raspberry Pi 1 Model A | BCM2835 |
Raspberry Pi 1 Model A+ | BCM2835 |
Raspberry Pi 1 Model B | BCM2835 |
Raspberry Pi 1 Model B+ | BCM2835 |
Raspberry Pi Zero | BCM2835 |
Raspberry Pi 2 Model B | BCM2836 |
Důležité je si uvědomit, že zatímco hlavní procesor je v čipu BCM2835 (jednojádrový ARM11) odlišný od procesoru použitého v čipu BCM2836 (čtyřjádrový ARM Cortex-A7), samotný grafický procesor má prakticky stejné vlastnosti, což se týká jak maximálních podporovaných rozlišení, tak i možností jeho naprogramování. I z tohoto důvodu v dalším textu nebudu rozlišovat mezi jednotlivými modely Raspberry Pi.
Poznámka: všechny příklady byly odzkoušeny na Raspberry Pi Model B+ se systémem Raspbian.
Programovatelnost GPU čipů BCM2835 a BCM2836 je v současnosti omezena tím, že základní funkcionalita je poskytována binárním blobem (který teoreticky může obsahovat jakýkoli kód). Až nad tímto blobem se nachází open source driver, který poskytuje své rozhraní dalším knihovnám (EGL, OpenMax) či přímo aplikacím.
4. Přístup k framebufferu na nejnižší úrovni (mailboxy a přímý přístup do videopaměti)
GPU, který je implementován na čipech BCM2835 a BCM2836, obsahuje i programovatelný video řadič založený na použití klasického framebufferu. Zjednodušeně řečeno lze říci, že video řadič postupně načítá z nadefinované oblasti fyzické paměti data, tato data interpretuje jako barvy pixelů a následně tyto barvy posílá společně se synchronizačními signály na HDMI popř. je slučuje do kompozitního video signálu posílaného na konektor RCA (či na jack u některých modelů). Video řadič je tedy nejprve nutné nakonfigurovat – předat mu informace o framebufferu a taktéž informace potřebné pro správné časování signálů posílaných na monitor. Na nejnižší úrovni se tyto informace předávají přes takzvané mailboxy, které si můžeme představit jako malé fronty (FIFO) s kapacitou osmi slov o šířce 32 bitů. Do těchto front může z jedné strany zapisovat procesor (ARM) a z druhé strany může zprávy vybírat GPU či naopak.
Nastavit či přečíst lze tyto informace:
- Alokace a dealokace paměti pro framebuffer (čistě teoreticky lze tuto operaci provést i za běhu systému, prakticky se provádí jednou při bootování)
- Nastavení fyzického rozlišení (nemusí odpovídat virtuálnímu rozlišení, GPU může provést změnu měřítka)
- Nastavení virtuálního rozlišení (může být menší než fyzické rozlišení, potom lze obrazem posouvat či měnit měřítko)
- Nastavení barevné hloubky (počtu bitů na pixel)
- Specifikace kódování pixelů (zde lze jen volit mezi BGR a RGB)
- Specifikace alfa kanálu (ignorován, použit, použit, ale s invertovanými hodnotami)
- Nastavení barvové palety
- Nastavení offsetu mezi obrazovými řádky (takzvaný pitch)
- Konfigurace kurzoru (autor článku ještě bude muset prozkoumat, jestli tato volba skutečně funguje :-)
5. Využití Linux framebufferu
Přístup ke GPU přes mailboxy není zcela triviální, protože je nutné zajistit, aby se zapisované hodnoty neukládaly pouze do cache, ale skutečně se „propsaly“ až do fyzického řídicího registru. Taktéž je nutné testovat, zda GPU potvrdil operaci či vrátil požadovanou hodnotu atd. Z tohoto důvodu se prozatím použití mailboxů vyhneme a využijeme namísto toho služeb poskytovaných kernelem. Kernel sám zajišťuje nízkoúrovňové ovládání framebufferu a v uživatelském prostoru (kde budou spouštěny naše demonstrační příklady) se k funkcím kernelu souvisejícím s framebufferem dostaneme přes speciální zařízení /dev/fb0. Přístup k tomuto zařízení mají všichni uživatelé ve skupině video, takže si pro jistotu nechte vypsat:
ls -la /dev/fb0 crw-rw---- 1 root video 29, 0 led 20 19:28 /dev/fb0
(důležité je jméno skupiny, tedy skutečně video)
Dále je vhodné se ujistit, že aktuální uživatel je skutečně přidán do skupiny video (což by v Raspbianu mělo být splněno):
groups pi adm cdrom sudo dip video plugdev lpadmin sambashare
Pokud aktuální uživatel není ve skupině video, stačí ho tam přidat (a znovu se přihlásit! nebo použít newgrp), popř. spouštět všechny další příkazy a příklady pod právy roota (su/sudo, nicméně doporučuji spíše první možnost).
Se zařízením /dev/fb0 lze provádět tři základní operace:
- Čtením se získají barvy jednotlivých pixelů.
- Zápisem se mění barvy pixelů (postupně řádek po řádku).
- Operací ioctl se získávají další údaje o framebufferu či se naopak mění jeho konfigurace. Díky existenci této operace bylo možné ponechat nízký počet služeb kernelu, protože většina pokročilejších operací se provádí právě přes ioctl aplikované na nějaké speciální zařízení.
Podívejme se nyní na jednoduchý a relativně neškodný příklad – jak na obrazovku vykreslit barevný šum a současně si vizuálně otestovat pomalost generátoru náhodných čísel. Na konzoli (na Raspbianu ale klidně i v X Window) si zkuste pustit následující příkaz:
cp /dev/urandom /dev/fb0 cp: error writing ‘/dev/fb0’: No space left on device cp: failed to extend ‘/dev/fb0’: No space left on device
Obrazovka by se skutečně měla pomalu zaplnit barevnými pixely a nakonec by se do tohoto zmatku měly vypsat dva řádky informující o tom, že framebuffer je zaplněn (což vidíme) a že ho nelze (logicky) zvětšit. Překreslení obrazovky do původního stavu zajistí příkaz clear, pro obnovení okrajů pak přepnutí do druhé konzoly a zpět (Alt+F2, Alt+F1 atd.).
Pokud si chcete udělat screenshot obrazovky ve formátu „raw“ (což vlastně není žádný formát, jen otisk pixelů), postačuje provést:
cp /dev/fb0 framebuffer.raw
6. První demonstrační příklad: přečtení základních informací o framebufferu
Čtení a zápis barev pixelů tedy dokážeme provést i přímo z shellu (což by čistě teoreticky mohlo některým uživatelům dostačovat například pro implementaci prográmku, který bude na základě stavu čidel připojených na GPIO zobrazovat nějaké jednodušší grafy – takový dotaz již na Rootu zazněl). Podívejme se nyní na zbývající operaci, tedy na ioctl (Input/Output Control), kterou již budeme volat z céčkového programu. Jak již víme z předchozího textu, lze použitím této funkce například získat informace o framebufferu. Konkrétně je tato informace vrácena v datové struktuře pojmenované fb_var_screeninfo, která má tento formát (schválně jsem ponechal i původní komentáře):
struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible */ __u32 yoffset; /* resolution */ __u32 bits_per_pixel; /* guess what */ __u32 grayscale; /* 0 = color, 1 = grayscale, */ /* >1 = FOURCC */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ __u32 nonstd; /* != 0 Non standard pixel format */ __u32 activate; /* see FB_ACTIVATE_* */ __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */ __u32 right_margin; /* time from picture to sync */ __u32 upper_margin; /* time from sync to picture */ __u32 lower_margin; __u32 hsync_len; /* length of horizontal sync */ __u32 vsync_len; /* length of vertical sync */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* angle we rotate counter clockwise */ __u32 colorspace; /* colorspace for FOURCC-based modes */ __u32 reserved[4]; /* Reserved for future compatibility */ };
Jak se tato struktura získá v céčkovém programu?
- Otevřeme zařízení /dev/fb0 v režimu čtení (O_RDONLY) funkcí open().
- Zavoláme funkci ioctl(), které se předají tři parametry: handle zařízení (hodnota získaná přes open() v předchozím kroku), konstanta FBIOGET_VSCREENINFO a ukazatel na strukturu typu fb_var_screeninfo. Funkce ioctl() naplní všechny položky této struktury a vrátí nulovou hodnotu. V případě nějaké chyby vrátí nenulovou hodnotu, kterou ihned otestujeme.
- Zavřeme zařízení /dev/fb0 funkcí close().
Podívejme se nyní, jak by mohl vypadat úplný demonstrační příklad, v němž navíc provádíme kontroly, zda operace open() a ioctl() proběhly v pořádku. Povšimněte si, které hlavičkové soubory je nutné použít (všechny by měly být dostupné ve výchozí instalaci Raspbianu, pokud bude překladač hlásit chyby, vyřešíme v komentářích pod článkem):
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/fb.h> /* Datova struktura, do niz se ulozi informace o framebufferu. */ typedef struct fb_var_screeninfo FramebufferInfo; /* Vypis zakladnich informaci o framebufferu. */ void printFramebufferInfo(int framebufferDevice) { FramebufferInfo framebufferInfo; /* Pokud operace ioctl probehne v poradku, vrati se 0 */ if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &framebufferInfo)) { perror("Nelze precist informace o framebufferu"); return; } /* Nyni je datova struktura FramebufferInfo naplnena. */ printf("Realne rozliseni: %dx%d\n", framebufferInfo.xres, framebufferInfo.yres); printf("Virtualni rozliseni: %dx%d\n", framebufferInfo.xres_virtual, framebufferInfo.yres_virtual); printf("Bitu na pixel: %d\n", framebufferInfo.bits_per_pixel); } int main(int argc, char **argv) { int framebufferDevice = 0; /* Ze zarizeni potrebujeme pouze cist.*/ framebufferDevice = open("/dev/fb0", O_RDONLY); /* Pokud otevreni probehlo uspesne, nacteme * a nasledne vypiseme informaci o framebufferu.*/ if (framebufferDevice != -1) { printFramebufferInfo(framebufferDevice); close(framebufferDevice); return 0; } /* Otevreni se nezadarilo, vypiseme chybove hlaseni.*/ else { perror("Nelze otevrit ovladac /dev/fb0"); return 1; } }
Překlad se provede tím nejjednodušším možným způsobem:
gcc -o rpi_fb1 rpi_fb1.c
Spuštění:
./rpi_fb1
Na mém Raspberry Pi se konkrétně vypíše:
Realne rozliseni: 1280x1024 Virtualni rozliseni: 1280x1024 Bitu na pixel: 16
U notebooku (který má samozřejmě taktéž framebuffer) pak:
Realne rozliseni: 1440x900 Virtualni rozliseni: 1440x900 Bitu na pixel: 32
7. Druhý demonstrační příklad: přečtení identifikace čipu, velikosti framebufferu a délky obrazového řádku
Ve skutečnosti v datové struktuře fb_var_screeninfo popsané a použité v předchozí kapitole nezískáme všechny informace nutné pro ovládání framebufferu. Další sada konfiguračních parametrů je dostupná ve struktuře nazvané fb_fix_screeninfo. Zajímavé je, že zde nalezneme i identifikaci GPU, ovšem důležitější jsou údaje o skutečném délku obrazového řádku (v bajtech) a celkové velikosti framebufferu:
struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len; /* Length of frame buffer mem */ __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */ unsigned long mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Indicate to driver which */ /* specific chip/card we have */ __u16 capabilities; /* see FB_CAP_* */ __u16 reserved[2]; /* Reserved for future compatibility */ };
Způsob naplnění této datové struktury je ukázán v dnešním druhém demonstračním příkladu. Povšimněte si, že postup je naprosto shodný s předchozím příkladem, ovšem druhý a třetí parametr předaný do funkce ioctl() je odlišný. Druhým parametrem je konstanta FBIOGET_FSCREENINFO (pozor na to, že rozdíl je jen v jediném znaku!) a třetím parametrem pak ukazatel na datovou strukturu typu fb_fix_screeninfo (nikoli fb_var_screeninfo):
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/fb.h> /* Datova struktura, do niz se ulozi informace o framebufferu. */ typedef struct fb_var_screeninfo FramebufferInfo; typedef struct fb_fix_screeninfo ModeInfo; void printFramebufferInfo(int framebufferDevice) { FramebufferInfo framebufferInfo; ModeInfo modeInfo; /* Pokud operace ioctl probehne v poradku, vrati se 0 */ if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &framebufferInfo)) { perror("Nelze precist informace o framebufferu"); return; } /* Nyni je datova struktura FramebufferInfo naplnena. */ printf("Realne rozliseni: %dx%d pixelu\n", framebufferInfo.xres, framebufferInfo.yres); printf("Virtualni rozliseni: %dx%d pixelu\n", framebufferInfo.xres_virtual, framebufferInfo.yres_virtual); printf("Bitu na pixel: %d bitu\n", framebufferInfo.bits_per_pixel); if (ioctl(framebufferDevice, FBIOGET_FSCREENINFO, &modeInfo)) { perror("Nelze precist informace o rezimu"); return; } /* Nyni je datova struktura ModeInfo naplnena. */ printf("Identifikace: %s\n", modeInfo.id); printf("Delka obrazoveho radku: %d bajtu\n", modeInfo.line_length); printf("Velikost framebuffer: %d bajtu\n", modeInfo.smem_len); } int main(int argc, char **argv) { int framebufferDevice = 0; /* Ze zarizeni potrebujeme pouze cist.*/ framebufferDevice = open("/dev/fb0", O_RDONLY); /* Pokud otevreni probehlo uspesne, nacteme * a nasledne vypiseme informaci o framebufferu.*/ if (framebufferDevice != -1) { printFramebufferInfo(framebufferDevice); close(framebufferDevice); return 0; } /* Otevreni se nezadarilo, vypiseme chybove hlaseni.*/ else { perror("Nelze otevrit ovladac /dev/fb0"); return 1; } }
Překlad druhého příkladu se opět provede tím nejjednodušším možným způsobem:
gcc -o rpi_fb2 rpi_fb2.c
Spuštění pak příkazem:
./rpi_fb2
Na mém Raspberry Pi se konkrétně vypíše (povšimněte si detekce identifikace GPU, podle mě není zcela přesné):
Realne rozliseni: 1280x1024 pixelu Virtualni rozliseni: 1280x1024 pixelu Bitu na pixel: 16 bitu Identifikace: BCM2708 FB Delka obrazoveho radku: 2560 bajtu Velikost framebuffer: 2621440 bajtu
Na notebooku s GPU od firmy Intel se vypíše:
Realne rozliseni: 1440x900 pixelu Virtualni rozliseni: 1440x900 pixelu Bitu na pixel: 32 bitu Identifikace: inteldrmfb Delka obrazoveho radku: 5760 bajtu Velikost framebuffer: 5185536 bajtu
8. Třetí demonstrační příklad: přístup do framebufferu s využitím mmap
Informace, které již o framebufferu umíme získat, nyní využijeme k přístupu k jednotlivým pixelům. Zde musíme použít další systémovou funkci, která je velmi užitečná. Jedná se o funkci nazvanou mmap(), k níž existuje i protějšek munmap(). Funkce mmap() dokáže namapovat „obsah“ nějakého souboru či zařízení do paměti, takže se s touto pamětí může pracovat stejně jako s jakoukoli jinou oblastí paměti s využitím ukazatelů atd. Funkce munmap() naopak toto mapování odstraní.
Volání funkce mmap() bude v našem konkrétním případě vypadat takto:
char *pixels = (char*)mmap( /* přetypování výsledku funkce mmap() */ 0, /* necháme na rozhodnutí jádra, aby zvolilo první adresu */ bufferSize, /* velikost namapované oblasti, zde velikost framebufferu (fb_fix_screeninfo.smem_len) */ PROT_READ | PROT_WRITE, /* budeme číst i zapisovat barvy pixelů */ MAP_SHARED, /* změna ve speciálním zařízení bude viditelná v celém systému */ framebufferDevice, /* handle vrácený funkcí open() */ 0); /* offset v rámci framebufferu, my k němu budeme přistupovat od začátku */
Jakmile funkce mmap() vrátila ukazatel na paměť s namapovaným speciálním zařízením, můžeme snadno změnit hodnoty všech pixelů, a to nezávisle na jejich struktuře (uložení barev atd.):
int x; for (x=0; x<bufferSize; x++) pixels[x] = x;
popř. pokud preferujete použití ukazatelů:
int x; char *p = pixels; for (x=0; x<bufferSize; x++) *p++ = x;
Na závěr je nutné funkcí munmap() zrušit mapování. Podívejme se nyní na úplný zdrojový kód tohoto příkladu:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <linux/fb.h> /* Datova struktura, do niz se ulozi informace o framebufferu. */ typedef struct fb_var_screeninfo FramebufferInfo; typedef struct fb_fix_screeninfo ModeInfo; long int printFramebufferInfo(int framebufferDevice) { FramebufferInfo framebufferInfo; ModeInfo modeInfo; /* Pokud operace ioctl probehne v poradku, vrati se 0 */ if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &framebufferInfo)) { perror("Nelze precist informace o framebufferu"); return 0; } /* Nyni je datova struktura FramebufferInfo naplnena. */ printf("Realne rozliseni: %dx%d pixelu\n", framebufferInfo.xres, framebufferInfo.yres); printf("Virtualni rozliseni: %dx%d pixelu\n", framebufferInfo.xres_virtual, framebufferInfo.yres_virtual); printf("Bitu na pixel: %d bitu\n", framebufferInfo.bits_per_pixel); if (ioctl(framebufferDevice, FBIOGET_FSCREENINFO, &modeInfo)) { perror("Nelze precist informace o rezimu"); return 0; } /* Nyni je datova struktura ModeInfo naplnena. */ printf("Identifikace: %s\n", modeInfo.id); printf("Delka obrazoveho radku: %d bajtu\n", modeInfo.line_length); printf("Velikost framebuffer: %d bajtu\n", modeInfo.smem_len); return modeInfo.smem_len; } void draw(int framebufferDevice, long int bufferSize) { char *pixels = (char*)mmap(0, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, framebufferDevice, 0); if (pixels != MAP_FAILED) { int x; for (x=0; x<bufferSize; x++) pixels[x] = x; getchar(); munmap(pixels, bufferSize); } else { perror("Nelze pristupovat k framebufferu"); } } int main(int argc, char **argv) { int framebufferDevice = 0; /* Ze zarizeni potrebujeme pouze cist.*/ framebufferDevice = open("/dev/fb0", O_RDWR); /* Pokud otevreni probehlo uspesne, nacteme * a nasledne vypiseme informaci o framebufferu.*/ if (framebufferDevice != -1) { long int bufferSize = printFramebufferInfo(framebufferDevice); if (bufferSize) { draw(framebufferDevice, bufferSize); } close(framebufferDevice); return 0; } /* Otevreni se nezadarilo, vypiseme chybove hlaseni.*/ else { perror("Nelze otevrit ovladac /dev/fb0"); return 1; } }
Překlad třetího příkladu se opět provede tím nejjednodušším možným způsobem:
gcc -o rpi_fb3 rpi_fb3.c
Spuštění pak příkazem:
./rpi_fb3
Realne rozliseni: 1280x1024 pixelu Virtualni rozliseni: 1280x1024 pixelu Bitu na pixel: 16 bitu Identifikace: BCM2708 FB Delka obrazoveho radku: 2560 bajtu Velikost framebuffer: 2621440 bajtu
Příště si ukážeme některé další triky s framebufferem i s použitím mailboxů. Již nyní je však možné na prezentovaném příkladu stavět jednodušší aplikace, které na Raspberry Pi využijí grafický výstup bez nutnosti používat plnohodnotné desktopové prostředí a komplikované knihovny.
9. Repositář s demonstračními příklady
Všechny tři demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly uloženy do Git repositáře umístěného na GitHubu na adrese (https://github.com/tisnik/presentations):
# | Příklad | Zdrojový kód |
---|---|---|
1 | rpi_fb1.c | https://github.com/tisnik/presentations/blob/master/rpi_framebuffer/rpi_fb1.c |
2 | rpi_fb2.c | https://github.com/tisnik/presentations/blob/master/rpi_framebuffer/rpi_fb2.c |
3 | rpi_fb3.c | https://github.com/tisnik/presentations/blob/master/rpi_framebuffer/rpi_fb3.c |
Pro překlad těchto tří demonstračních příkladů je zapotřebí mít nainstalován překladač GNU C (či Clang), linker a vývojářskou verzi libc.
10. Funkce použité v demonstračních příkladech
Popis funkcí (ve skutečnosti se vlastně jedná pouze o pětici systémových volání), které byly použity v dnešních demonstračních příkladech, naleznete buď ve druhé sekci manuálových stránek (man 2 jméno_funkce), popř. pokud preferujete použití webového prohlížeče se lze na adrese https://www.kernel.org/doc/man-pages/ podívat na ty samé manuálové stránky, ovšem zkonvertované do formátu HTML:
# | Funkce | Popis |
---|---|---|
1 | open() | man 2 open |
2 | close() | man 2 close |
3 | ioctl() | man 2 ioctl |
4 | mmap() | man 2 mmap |
5 | munmap() | man 2 unmap |
11. Odkazy na Internetu
- Raspberry Pi pages
https://www.raspberrypi.org/ - BCM2835 registers
http://elinux.org/BCM2835_registers - VideoCore (archiv stránek společnosti Alphamosaic)
http://web.archive.org/web/20030209213838/www.alphamosaic.com/videocore/ - VideoCore (Wikipedia)
https://en.wikipedia.org/wiki/Videocore - RPi lessons: Lesson 6 Screen01
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html - Raspberry Pi forum: Bare metal
https://www.raspberrypi.org/forums/viewforum.php?f=72 - C library for Broadcom BCM 2835 as used in Raspberry Pi
http://www.airspayce.com/mikem/bcm2835/ - Raspberry Pi Hardware Components
http://elinux.org/RPi_Hardware#Components - (Linux) Framebuffer
http://wiki.linuxquestions.org/wiki/Framebuffer - (Linux) Framebuffer HOWTO
http://tldp.org/HOWTO/Framebuffer-HOWTO/ - Linux framebuffer (Wikipedia)
https://en.wikipedia.org/wiki/Linux_framebuffer - RPi Framebuffer
http://elinux.org/RPi_Framebuffer - HOWTO: Boot your Raspberry Pi into a fullscreen browser kiosk
http://blogs.wcode.org/2013/09/howto-boot-your-raspberry-pi-into-a-fullscreen-browser-kiosk/ - Zdrojový kód fb.c pro RPI
https://github.com/jncronin/rpi-boot/blob/master/fb.c - RPiconfig
http://elinux.org/RPi_config.txt - Mailbox framebuffer interface
https://github.com/raspberrypi/firmware/wiki/Mailbox-framebuffer-interface