Produkty Novinky Články Návody Kontakty

SAM9260 a grafický TFT displej - 6. linuxový ovladač - dokončení

Minulý díl byl zaměřen na stručný popis konfigurace a režimů komunikace řadiče displeje SSD1963. Také jsme se už pustili do implementace základů ovladače pro systém Linux, který by umožňoval zobrazovat data na TFT displeji INT070ATFT-TS pomocí procesorového modulu SAM9260. Dnes ovladač dokončíme a vyzkoušíme, jestli opravdu funguje.
Rovnou se pustím do inicializace TFT displeje, resp jeho řadiče SSD1963. Inspiraci jsem hledal samozřejmě v datasheetu řadiče SSD1963. Mnohem lepším zdrojem se však ukázaly být příklady připojení řadiče SSD1963 k mikrokontroléru STM32 a příklad, který mi na požádání zaslal obchodník z Displaytechu. Výsledek je takový hezký kompilát mých poznatků a uvedených příkladů, který je realizován ve funkci init_tft(). Nemá cenu zde vypisovat její zdrojový kód, protože jednotlivé kroky inicializace jsem naprogramoval jako samostatné funkce. Raději si stáhněte už hotový zdrojový kód ovladače int070atft-ts a prostudujte si ho. Já jen v jednotlivých bodech nastíním co a proč jsem dělal, když už jsem něco dělal :)
Inicializace:
  1. První krokem je tradičně reset. Použil jsem pro jednoduchost softwarový reset viz funkce soft_reset(). V datasheetu se zmínili, že je slušnost počkat alespoň 5ms než budeme chtít po TFT displeji nějakou další aktivitu. Součástí zmíněné funkce je tedy i čekání. Chvílí jsem přemýšlel i o ovládání signálu /RESET pomocí I/O pinu modulu SAM9260, ale nenašel jsem žádný důvod, proč to řešit takto.
  2. Konfigurace PLL je po zapnutí napájení dána automatickou inicializací TFT displeje (sw reset konfiguraci PLL ponechává nedotčenou). Bohužel hodnoty, na kterých PLL běží, jsou mi neznámé, proto PLL přenastavuji podle sebe. Funkce set_pll_mn() nastaví násobiče a děliče tak, aby výstupní frekvence PLL byla 100MHz při vstupním kmitočtu 8MHz generovaném vnějším krystalem. Pak je ještě potřeba ve funkci start_pll() aktivovat PLL a po 100 mikrosekundách, kdy je už PLL bezpečně zavěšené, ho nastavit jako zdroj systémových hodin.
  3. Ve funkci set_lshift_freq() se nastavuje tzv. pixel clock - rychlost s jakou jsou vykreslovány/obnovovány jednotlivé obrazové body (pixely). Je odvozena od frekvence PLL a spolu s parametry hsync a vsync (viz dále) určuje obnovovací frekvenci TFT panelu - četnost překreslování celého obrazu. Já jsem zvolil doporučenou hodnotu pixel clocku (PCLK) 30MHz.
  4. Ve funkci set_lcd_mode() definuji řadiči SSD1963 parametry připojeného zobrazovacího elementu (TFT panelu). Jde o šíři datové sběrnice mezi řadičem SSD1963 a zobrazovacím elementem (TFT panelem), polaritu řídících signálů na tomto rozhraní, fyzickým rozlišením TFT panelu apod. Funkci set_address_mode() pak nastavuje mapování framebufferu na obrazové body TFT panelu. Zvolil jsem výchozí variantu: počátek framebufferu odpovídá levému hornímu rohu TFT panelu, vykresluje se řádek po řádku až dolů. Jednotlivý řádek se kreslí zleva doprava. Stejně tak i při obnově obrazu.
  5. Funkce set_pixel_data_interface() řekne řadiči SSD1963, že pro obrazová data se bude používat 16bitová datová sběrnice a data pro jeden pixel se budou přenášet naráz v jednom cyklu. Funkce set_pixel_format() nastavuje počet bitů pro barevnou hloubku. V našem případě 16bitů a formát RGB565. Pikantní je, že použitý příkaz 0x3A je označen v datasheetu jako reserved.
  6. Dvojice funkcí set_hori_period() a set_vert_period() nastavuje parametry, které souvisí se samotným vykreslováním jednotlivých řádků (hsync) a celého obrazu (vsync). Hsync parametry se udávají v počtech taktů pixel clocku, vsync pak v počtu vykreslených řádků. Jde o parametry horizontal total period, hsync pulse start position (hsync back porch), hsync pulse width, vertical total period, vsync pulse start position (vsync back porch) a vsync pulse width. Zájemcům o bližší pochopení doporučuji nastudovat problematiku zobrazování na klasických CRT obrazovkách. Pojmy i jejich význam je stejný.
  7. Pomocí dvojice funkcí set_column_address() a set_page_address() nastavuji řadiči oblast ve framebufferu, kam může následně mikrokontrolér zapisovat (anebo odsud číst) obrazová data pomocí příkazů write_memory_start či write_memory_continue. První ze jmenovaných funkcí vymezí oblast v ose x, druhá v ose y; adresy se udávají v pixelech. Cokoliv zapsané mimo tuto oblast je ignorováno. Je logické, že v inicializační funkci nastavíme oblast na celý displej tedy rozsah adres na ose x od 0 do 799 a na ose y od 0 do 479. Při přístupu do framebufferu mějte ovšem na paměti nastavení dané funkcí set_address_mode() - mapování obsahu framebufferu na obrazové body.
  8. Řadič SSD1963 dává k dispozici čtyři GPIO piny. Nepoužívám je, ale podle rozkazu z datasheetu, je potřeba jim nastavit směr, pokud nejsou používány pro potřeby TFT panelu. Pomocí funkce set_gpio_value() je nejprve nastavím na log. 1 a pak je v set_gpio_conf() nakonfiguruji jako běžné výstupní GPIO.
  9. A jdeme do finále - funkce set_display_on() přepne TFT displej ze stavu Sleep (výchozí stav po resetu) do stavu Display. Ovšem dokud nezapneme podsvícení, tak stejně není nic vidět.
  10. No když už jsme u toho podsvícení - DC-DC měnič, který generuje napájení pro skupinu podsvětlovacích LED diod, je řízen pulsně šířkovou modulací (PWM). Je tedy třeba nastavit frekvenci, na které měnič pracuje, a šířku pulsu (modulační poměr), která udává intenzitu podsvícení. O to se stará funkce set_pwm_conf(), která jako parametr přijímá hodnotu jasu (0 - 255). Pracovní frekvenci jsem po několika pokusech nastavil na 300Hz. Jas bude řízen z mikrokontroléru. Je možné ještě nechat řídit jas řadičem SSD193 pomocí tzv. funkce dynamic brightness control (DBC), která analyzuje obrazová data a přizpůsobuje jim jas, ale to jsem nechtěl. No a od tohoto místa bychom měli ”něco” vidět na TFT panelu.
  11. Přestože funkci DBC nepoužívám, tak ve funkci set_dbc_conf() nastavuji DBC alespoň základní parametry. Co kdyby, že?
  12. No a poslední konfigurační funkce set_post_proc() umožňuje upravit jas, kontrast a saturaci obrazu. Oproti výchozímu stavu jsem všechny parametry trochu ”vytáhnul” nahoru.
No tož inicializace je hotová, takže nyní kompilace modulu, zavedení do jádra a zkouška. Modul jsem kompiloval mimo strom jádra vůči jádru 2.6.38, který jsem opatřil patchem at91 a patchem ucsimply-sam9260 pro modul SAM9260. Kdo neví jak na to, nechť si přečte návod, jak aplikovat patche. Jádro samotné je po aplikaci patchů potřeba nakonfigurovat pro modul SAM9260 a buď zkompilovat komplet nebo jen do stavu vhodného pro kompilaci modulů jádra. Kompilaci jsem prováděl cross-kompilátorem CodeBench GNU/Linux 2010.09-50 od Mentor Graphics (dříve CodeSourcery). Níže uvádím sadu příkazů, které připraví jádro opatřené patchi pro kompilaci modulu:
$ export ARCH=arm
$ export CROSS_COMPILE=arm-none-linux-gnueabi-
$ make distclean
$ make ucsimply_sam9260_defconfig
$ make modules_prepare
Modul samotný se kompiluje klasicky příkazem make v adresáři se zdrojovým kódem modulu. Jen je potřeba si pohlídat v Makefile cestu ke stromu jádra 2.6.38. Výsledkem kompilace je soubor int070atft-ts.ko. Tento soubor je potřeba nějak přenést do modulu SAM9260 nabootovaného do Linuxu. Já jsem využil souborový systém NFS, kdy jsem si nasdílel adresář v mém počítači, tam jsem nakopíroval modul ovladače int070atft-ts.ko a pomocí NFS klienta si jej zpřístupnil v souborovém systému modulu SAM9260. Pak už jen stačilo příkazem insmod int070atft-ts.ko zavést modul do jádra a bylo to. TFT displej se rozsvítil (ha, podsvícení funguje) a objevila se mozaika různě barevných bodů. Prostě stejně jako když proběhl automatický init TFT displeje. I vyjmul jsem modul z jádra příkazem rmmod int070atft-ts, tlačítkem SW manuálně zresetoval TFT displej (zhasnul) a začal vymýšlet funkci, která by do framebufferu řadiče displeje zapsala nějaká výchozí obrazová data - třeba jednolitou barvu.
Výsledkem mého snažení byla funkce tft_clear(), kterou jsem přidal na konec inicializace TFT displeje.
static void tft_clear(unsigned int color)
{ 	
	set_column_address(TFT_SC, TFT_EC);
	set_page_address(TFT_SP, TFT_EP);
	
	// dame SSD1963 vedet, ze budeme ted zapisovat do framebufferu
	write_cmd(CMD_WR_MEMSTART);
	// zapiseme kod barvy na celou plochu displeje
	write_data_rep(color, (TFT_ROWS * TFT_COLS));
}
Na kódu funkce tft_clear() je krásně vidět, jak se vlastně zapisují obrazová data do TFT displeje. Na začátku si vždy nastavíme oblast framebufferu, do které budeme ukládat data. Této oblasti pak samozřejmě podle nastavení adresového módu (mapování) odpovídá i oblast zobrazení na TFT panelu. V této funkci je to jasné - chci pokrýt celý TFT panel. Pak řadiči oznámíme příkazem write_memory_start, že si má vyresetovat ukazatel sloupců a řádků a že následující data budou obrazová data. No pak už jen v cyklu zapíšeme do celé oblasti ve framebufferu, tj. pro každý pixel TFT panelu, 16bitový kód příslušné barvy ve schématu RGB565. Volání této funkce jsem přidal na závěr inicializace TFT displeje. Mnou zvolená barva byla bílá inspirovaná padajícím sněhem.
Ale aby byla ještě větší zábava, tak jsem funkci tft_clear() přidal i do souborové operace int070atft_write() s tím, že první dva byty zapsané do ovladače z uživatelského prostoru budou předány funkci tft_clear() jako kód barvy. tak budu moct snadno zápisem dat do ovladače z uživatelského prostoru měnit bravu celé plochy TFT panelu. Takže kompilace, přenesení ovladače na modul SAM9260, zavedení a zkouška změny barvy pomocí příkazů:
# fialova
echo -en '\x1f\xf8' > /dev/int070atft-ts

# zelena
echo -en '\xe0\x07' > /dev/int070atft-ts
Fungovalo to na jedničku - viz obrázek Krásná zelená. Další hrátky s TFT displejem uveřejním někdy přístě.
Krásná zelená

Seznam dílů seriálu SAM9260 a grafický TFT displej