Pořídit si z Arduina výstup na TV přijímač nebo videomonitor není jen pro ty, kdo chtějí zkoušet psát jednoduché hry, ale je to praktický a kupodivu velmi levný prostředek, který poslouží při psaní programů a odlaďování funkce zařízení. Tentokrát se podíváme na konkrétní příkazy knihovny TVout a příklady použití.
Příkazy
Přičleníme-li knihovnu TVout, vytvoří se v našem programu na začátku dva řádky:
#include <TVout.h>
#include <video_gen.h>
a my případně (budeme-li chtít využívat různé rozměry písma) přidáme třetí s přičleněním fontů:
#include <fontALL.h>
Následně můžeme používat mimo jiné tyto příkazy. Jako vždy je důležité rozlišení velkých a malých písmen.
-
TVout jmeno; – vytvoření objektu jmeno třídy TVout (píše se typicky mezi deklarace proměnných)
-
jmeno.begin(video); – parametr video nabývá hodnot _PAL nebo _NTSC. Nastaví zadanou normu a spustí generování obrazu
-
jmeno.clear_screen(); – smaže obsah displeje (celá pracovní plocha černá)
-
jmeno.invert(); – invertuje celou pracovní plochu displeje
-
jmeno.set_pixel(x,y,barva); – vykreslí bod v absolutních souřadnicích x,y. Parametr barva: 0 – černá,1 – bílá, 2 – inverzně. Souřadnice (parametry) jsou 0-127 a 0-95, takže typicky typu int.
-
jmeno.get_pixel(x,y); – funkce vrací barvu bodu v daném bodě x,y: 0–černá, 1-bílá
-
jmeno.draw_line(x1,y1,x2,y2,barva); – vykreslí čáru v absolutních souřadnicích od bodu x1,y1 do bodu x2,y2. Parametr barva: 0–černá,1–bílá, 2-inverzně
-
jmeno.draw_row(y,x0,x1,barva); – velmi rychlevykreslí vodorovnou čáru od bodu x0,y do bodu x1,y barvou 0–černá,1–bílá, 2-inverzně
-
jmeno.draw_column(x,y0,y1,barva);– velmi rychle vykreslí svislou čáru od bodu x,y0 do bodu x,y1 barvou 0–černá,1–bílá, 2-inverzně
-
jmeno.draw_rect(x,y,sirka,vyska,barva,vypln); – vykreslí obdélník od bodu x,y (levý horní roh) o šířce sirka, a výšce vyska s barvou čáry (0–černá,1–bílá, 2-inverzně) a s výplní (0–černá,1–bílá, 2-inverzně,-1-nechat původní). Parametr vypln není povinný, bez něj se ponechá původní obsah plochy.
-
jmeno.draw_circle(x,y,polomer,barva,vypln); – vykreslí kružnici se středem v bodě x,y o poloměru polomer, s barvou čáry (0–černá,1–bílá, 2-inverzně) a s výplní (0–černá,1–bílá, 2-inverzně,-1-nechat původní). Parametr vypln není povinný.
-
jmeno.shift(vzdalenost,smer); – posune obsah displeje o počet bodů udaných parametrem vzdalenost ve směru 0-nahoru, 1-dolů, 2-vlevo, 3-vpravo. Obsah, který „vyjede“ ze zobrazovaného pole se ztrácí. Používá se typicky k „rolování“ obrazovky.
-
jmeno.print_char(x,y,chr); – od bodu x,y (levý horní roh znaku) vypíše znak s kódem chr
-
jmeno.set_cursor(x,y); – nastaví pozici textového kurzoru na bod x,y
-
jmeno.select_font(font); – nastaví font – parametr může být font4x6, font6x8, font8x8 nebo font8x8ext. Externí fonty lze předefinovat a vytvořit si tak v podstatě libovolnou grafiku.
-
jmeno.print(x,y,str); – vypíše na pozici x,y řetězec str, při přesahu přes kraj obrazovky přechází na další řádek
-
jmeno.print(str); – vypíše na pozici textového kurzoru řetězec str
-
jmeno.print(x,y,int,base); – vypíše od pozice x,y proměnnou typu int (unsigned int, long, unsigned long) udanou v zadané soustavě (parametr base: 2, 8, 10, 16). Převod funguje i pro jakýkoli jiný základ soustavy. Parametr base se nemusí uvádět, implicitní je desítková soustava.
-
jmeno.print(x,y,double,presnost); – vypíše na pozici x,y hodnotu proměnné typu double s přesností na daný počet desetinných míst, bez udání přesnosti na 2 desetinná místa. Pozice x,y není povinná, může pracovat podle poslední polohy kuzroru.
-
jmeno.delay_frame(pocet); – čeká daný počet snímků podle parametru počet, slouží k synchronizaci s vykreslováním obrazu
-
jmeno.tone(frekvence, delka); – jako generování tónu standardním tone, ale funguje správně i při generování obrazu a vždy je pro pin 11 u Arduina Uno
-
všechny příkazy print existují i ve verzi println s odřádkováním
Jednoduché příklady
První příklad pokreslí obrazovku sítí bodů (rozsvítí bíle sudé body v řádcích, ale jen na sudých řádcích) a potom se snaží cyklicky programem generovat pulzy na výstupu 5. Podobný program byl použit při snímání obrázku dokumentujícího vliv generování obrazu respektive interruptů na pravidelnost pulzů.
//video test - sude body v sudych radcich
#include <TVout.h> // knihovna TVout
#include <video_gen.h>
TVout TV; // objekt TV tridy TVout
void setup(){ // nastaveni
TV.begin(_PAL); // generovani signalu PAL
TV.clear_screen(); // smazani obrazovky
for (int i=0;i<96;i=i+2){ // cyklus pro radky
for (int j=0;j<128;j=j+2){ // cyklus pro body v radku
TV.set_pixel(j,i,1); // vykresleni bodu
}
}
pinMode(5,OUTPUT); // pin 5 na vystup
}
void loop(){ // opakovany program
digitalWrite(5,HIGH); // pulzy na vystup 5
digitalWrite(5,LOW); //
}
Další příklad smaže obrazovku, počká 1s, vykreslí bílý rámeček kolem celé pracovní plochy, pak 1s počká a vše se opakuje. Na pohled rámeček bliká.
//video test - blikajici ramecek
#include <TVout.h> // knihovna TVout
#include <video_gen.h>
TVout TV; // objekt TV tridy TVout
void setup(){ // nastaveni
TV.begin(_PAL); // generovani signalu PAL
}
void loop(){ // opakovany program
TV.clear_screen(); // smazani obrazovky
delay(1000); // pockat 1s
TV.draw_rect(0,0,127,95,1); // obdelnik kolem cele plochy
delay(1000); // pockat 1s
}
Třetí příklad vypíše největším možným písmem nahoru na obrazovku řadu číslic, pak počká, a nechá je plynule přejet a zmizet směrem dolů.
//video test - rolovani #include <TVout.h> // knihovna TVout #include <video_gen.h> #include <fontALL.h> // knihovny vsech fontu TVout TV; // objekt TV tridy TVout
void setup(){ // nastaveni
TV.begin(_PAL); // generovani signalu PAL
TV.select_font(font8x8); // nastaveni fontu
}
void loop(){ // opakovany program
TV.print(0,0,"1234567890"); // text vypsat na 0,0
delay(1000); // pockat 1s
for(int i=0;i<96;i++){ // 96x
TV.shift(1,1); // rolovat dolu o 1 bod
delay(1);} // doba mezi posuny
delay(1000); // pockat 1s
}
Čtvrtý příklad vypíše na obrazovku kousek textu a následně jej nechá po dvou sekundách přezrcadlit. Ukazuje zejména, že na obrazovku se dá nejen zapisovat, ale také z ní obsah číst a je-li to potřeba, třeba i odeslat na tiskárnu.
//video test - zrcadleni
#include <TVout.h> // knihovna TVout
#include <video_gen.h>
#include <fontALL.h> // knihovny vsech fontu
TVout TV; // objekt TV tridy TVout
int nahore, dole; // promenne pro prohozeni
void setup(){ // nastaveni
TV.begin(_PAL); // generovani signalu PAL
TV.select_font(font8x8); // nastaveni fontu
TV.print(0,0, "123456789012345"); // text vypsat
TV.print(0,12,"Toto je pokusny"); // text vypsat
TV.print(0,24,"text k ukazce "); // text vypsat
TV.print(0,36,"zrcadleni"); // text vypsat
}
void loop(){ // opakovany program
for(int i=0;i<47;i++){ // cyklus pro radky
for(int j=0;j<128;j++){ // cyklus pro sloupce
nahore=TV.get_pixel(j,i); // nacist horni bod
dole=TV.get_pixel(j,95-i); // nacist dolni bod
TV.set_pixel(j,i,dole); // ulozit horni bod
TV.set_pixel(j,95-i,nahore); // ulozit dolni bod
}
}
delay(2000); // pockat 2s
}
Pátý příklad představí více z možností grafiky jednoduchým (ale nikoli krátkým) programem. Cílem je nechat cyklicky střídat tři „obrazovky“. První ukáže základní vykreslené tvary, druhá fonty a třetí předvede náhodný „pohyb“ objektu v reálném čase. Nic se neovládá, zapojen je jen základní videoadaptér k Arduinu. Je-li to možné, nastavte televizní přijímač nebo monitor na poměr stran obrazu 4:3, pak jsou body téměř čtvercové a zejména kružnice vypadají jak mají.
//video1 - predvedeni grafiky
#include <TVout.h> // knihovna TVout
#include <video_gen.h>
#include <fontALL.h> // vsechny fonty
TVout TV; // objekt TV tridy TVout
int x1,y1,x2,y2; // pracovni promenne pro pohyb
float krok,krokx,kroky; // pracovni promenne pro pohyb
void setup(){ // nastaveni
TV.begin(_PAL); // generovani signalu PAL
TV.clear_screen(); // smazani obrazovky
}
void loop(){ // smycka programu
// Cast 1 - Obrazovka Zakladni tvary
TV.draw_rect(0,0,127,95,1); // ramecek kolem cele plochy
for (int i=0; i<7; i++){ // ctverce
TV.draw_rect(3*i+8,3*i+8,50-6*i,40-6*i,1);
}
for (int i=0; i<5; i++){ // kruznice
TV.draw_circle(100,28,i*5,1);
}
delay(500); // pockat 0,5s
for (int i=5; i<123; i++){ // cary invertujici
TV.draw_line(64,55,i,90,2);
delay(20);
}
delay(6000); // pockat 6s
for (int i=0; i<98; i++){ // odrolovani nahoru
TV.shift(1,0);
delay(5);
}
// Cast 2 - Obrazovka Fonty a vypis hodnot
TV.select_font(font8x8); // nastavit font 8x8
TV.print(0,0,"Font 8x8 - ABCD");
TV.select_font(font6x8); // nastavit font 6x8
TV.print(0,10,"Font 6x8 - ABCDEFGHIJ");
TV.select_font(font4x6); // nastavit font 4x6
TV.print(0,18,"Font 4x6 - ABCDEFGHIJKLMNOPQRST");
TV.select_font(font6x8); // dale font 6x8
for (int j=0; j<6; j++){ // vypis tabulky znaku
for (int i=0; i<16; i++){
TV.print_char(i*8,j*8+40,j*16+i+32);
delay(20);
}
}
delay(6000); // pockat 6s
for (int i=0; i<129; i++){ // odrolovani vlevo
TV.shift(1,2);
delay(5);
}
// Cast 3 - Obrazovka nahodny pohyb
TV.print(0,0,"Nahodny pohyb"); // nadpis
x1=64; y1=48; // pocatecni poloha
for (int i=0;i<20;i++){ // 20 pohybu
x2=random(0,120); // nove souradnice
y2=random(8,87);
krok=sqrt(pow(x2-x1,2)+pow(y2-y1,2)); // delka pohybu
krokx=(x2-x1)/krok; // krok pro x
kroky=(y2-y1)/krok; // krok pro y
for (int j=0;j<int(krok);j++){ // pohyb po krocich
TV.draw_rect(int(krokx*j+x1-1),int(kroky*j+y1),6,6,1);// vypsat
TV.delay_frame(1); // pockat a synchronizovat
TV.draw_rect(int(krokx*j+x1-1),int(kroky*j+y1),6,6,0);// smazat
}
x1=x2; y1=y2; // dalsi pohyb
}
for (int i=0; i<129; i++){ // odrolovani vpravo
TV.shift(1,3);
delay(5);
}
} // konec programu
Zatím uvedené příklady měly za úkol jen předvést možnosti a ukázat způsob zápisu, poslední bude mít trochu praktičtější použití. Doplníme zapojení o dva trimry, dvě tlačítka a piezoreproduktor. Jeden trimr vytvoří změny napětí, to budeme měřit a zobrazíme pomocí virtuálního ručkového měřidla. Druhý trimr udává svým napětím mezní povolenou hodnotu. Je-li jím stanovená mez na první vstupu překročena, oznámí se to jako chyba přerušovaným tónem. Chyba a její indikace trvá i když následně skončí důvody pro její vyhlášení. Jedno tlačítko resetuje sledování minima a maxima, druhé tlačítko ruší hlášení chyby. Žádná část měřidla nesmí za provozu viditelně blikat, tón musí znít čistě.
//video2 - analogove meridlo
#include <TVout.h> // knihovna TVout
#include <video_gen.h>
#include <fontALL.h> // vsechny fonty
TVout TV; // objekt TV tridy TVout
float krok=0.004887586; // prevod bit/napeti V
int U1,U2,U1s,U2s,Umax,Umin,Umez; // pracovni promenne napeti
unsigned long cykl; // pocitani cyklu
boolean chyba; // prekroceni meze napeti
void setup(){ // nastaveni
pinMode(2,INPUT_PULLUP); // vstup tlacitko
pinMode(3,INPUT_PULLUP); // vstup tlacitko
TV.begin(_PAL); // generovani signalu PAL
TV.clear_screen(); // smazani obrazovky
TV.draw_rect(0,0,127,95,1); // ramecek meridlo
for (int i=14;i<=114;i=i+2){ // stupnice male dilky
TV.draw_line(i,30,i,25,1);}
for (int i=14;i<=114;i=i+10){ // stupnice velke dilky
TV.draw_line(i,30,i,20,1);}
TV.select_font(font4x6); // nastavit font 4x6
for (int i=0;i<6;i++){ // popis meritka
TV.print(i*20+13,13,i);}
TV.select_font(font6x8); // nastavit font 6x8
TV.print(7,85,"Min"); // vypis Min
TV.print(75,85,"Max"); // vypis Max
Umax=0; Umin=1023; chyba=false; // pocatecni stavy
}
void loop(){ // smycka programu
U1=analogRead(5); // mereni napeti
U2=analogRead(4); // mereni meze
TV.draw_rect(13,32,102,52,0,0); // mazani rucky
TV.draw_line(64,85,map(U1,0,1023,14,114),32,1); // rucka
TV.draw_circle(64,85,3,1,1); // stred meridla
if (U1>Umax)Umax=U1; // kontrola mezi
if (U1<Umin)Umin=U1; //
TV.select_font(font6x8); // nastavit font 6x8
TV.print(28,85,float(Umin*krok)); // vypis Min
TV.print(98,85,float(Umax*krok)); // vypis Max
Umez=map(U2,0,1023,14,114); // vypocet meze
TV.set_pixel(Umez,32,1); // ukazatel meze
TV.draw_row(33,Umez-1,Umez+2,1); // ukazatel meze
if(digitalRead(2)==LOW){ // TL - mazani min a max
Umax=0; Umin=1023;}
if(digitalRead(3)==LOW) chyba=false; // TL - mazani chyby
if(U1>U2) chyba=true;
cykl++; // pocitani cyklu
if(chyba && cykl%25==0) TV.tone(800,250);// zvuk chyby
TV.delay_frame(1); // synchronizace
} // konec programu
Na tomto programu stojí za to si podrobněji všimnout nejen způsobu vytvoření virtuálních měřidel, ale ještě něčeho. Jen ty části měřidla, které se mění a tudíž musí překreslovat, se obsluhují v cyklu. Tím se výrazně šetří čas.
Další věcí je na první pohled nenápadná, ale velmi důležitá synchronizace, kterou má na starosti poslední příkaz TV.delay_frame . Pokud jej vypustíte, program samozřejmě poběží, ale uvidíte obrovský rozdíl. Programová smyčka trvá určitou dobu a pracuje v situaci, kdy se (naprosto pravidelně) odskakuje interruptem do zobrazení a přenáší obsah paměti na obrazovku. Když jsou tyto dva děje nezávislé na sobě, bude se stávat, že se například čára smaže těsně před tím, než by měla být zobrazena, pak proběhne zobrazení a čára se vykreslí do paměti v době, kdy už je po vykreslení na obrazovku. Důsledkem toho je, že čára není vidět, dokud se zase načasování zobrazení a běhu prohramu „nerozjede“. Na pohled se to projevuje jako velmi rušivé blikání některých nebo všech částí grafiky, těch, které jsou pravidelně překreslovány.
Celá smyčka našeho programu je schopná proběhnout v době mezi vykreslováním dat na obrazovku. Proto je na konci (mohlo by to být i na začátku) příkaz TV.delay_frame(1), který na rozdíl od „obyčejného“ delay nemá za úkol jen čekat, ale synchronizovat program podle zobrazení, čekat až proběhne vykreslení jednoho snímku na obrazovku (čas je udáván v těchto snímcích). Jeden průběh smyčky se dokončí dřív než se vykresluje, takže v době vykreslování už nedochází k žádným změnám a obraz je klidný, nebliká.
Pokud by byl program složitější a trval déle, než se dá mezi snímky stihnout, bylo by nutné „rozsekat“ více příkazy delay_frame program na více částí, které by se stihnout mezi snímky daly, přitom každá část by musela plně obsloužit své objekty (čáry, rámečky, body, …). Zajistit klidný výsledný obraz znamená nejen naprogramovat úlohu tak, aby vůbec fungovala, ale současně ji celou správně načasovat a synchronizovat s generováním obrazu.
Podobné je to s generováním zvuku. Obvyklý příkaz tone zde selže a vytvoří jen chrčení, je třeba použít příkazu z knihovny TV.tone, ten na daném pinu fungovat bude.
Závěr
Na stejných knihovnách obohacených využitím možnosti měnit externí fonty 8×8 bodů je postavena řada her dostupných na internetu – například Tetris, TV tenis nebo Life. To je jistě také zajímavé a víceméně se tím Arduino dostává do pozice, v níž byly před mnoha léty osmibitové počítače, jen je za nesrovnatelně nižší cenu. Jak už jsem uvedl na začátku potenciál spolupráce s televizí nebo videomonitorem ale vidím hlavně jinde, v názornějším výstupu dat z čidel při zkouškách zapojení, ve výpisech při ladění programu a podobně. Používáte-li Arduino, zkuste to, není to nijak složité a žádné náklady to nevyžaduje.



