Operácie

AP Blok 3: Rozdiel medzi revíziami

Zo stránky SensorWiki

Balogh (diskusia | príspevky)
Balogh (diskusia | príspevky)
 
(Jedna medziľahlá úprava od rovnakého používateľa nie je zobrazená.)
Riadok 163: Riadok 163:
''Tip:'' Aby ste mohli meniť parametre, musíte port najprv otvoriť.
''Tip:'' Aby ste mohli meniť parametre, musíte port najprv otvoriť.


===  
=== '''3.3 Vysielanie''' ===
'''3.3 Vysielanie''' ===


 
Zápis na port má na starosti funkcia <code>WriteFile</code>, pričom sa použijú aktuálne prenosové parametre. Funkcia uloží do premennej Pocet počet skutočne odvysielaných znakov, ktorý treba kontrolovať. V prípade zlyhania buď prenos zopakujeme, alebo skončíme s chybou.
Zápis na port má na starosti funkcia WriteFile, pričom sa použijú aktuálne prenosové parametre. Funkcia uloží do premennej Pocet počet skutočne odvysielaných znakov, ktorý treba kontrolovať. V prípade zlyhania buď prenos zopakujeme, alebo skončíme s chybou.


Nasledujúca ukážka samozrejme predpokladá, že port je otvorený a správne nakonfigurovaný.
Nasledujúca ukážka samozrejme predpokladá, že port je otvorený a správne nakonfigurovaný.
Riadok 190: Riadok 188:
</source>
</source>


Úloha: Napíšte program, ktorý bude na zvolený port stále dokola vysielať znak 'A'. Overte funkciu prijímaním v bežnom terminálovom programe, ako je napr. Hyperterminál.


Tip: Lepšie ako hyperterminál je napr. toto: Terminal by Bray++, neinštaluje sa, len spustí.
'''Úloha:''' Napíšte program, ktorý bude na zvolený port stále dokola vysielať znak 'A'. Overte funkciu prijímaním v bežnom terminálovom programe.
 
 
''Tip:'' Použite napr. Terminal by Bray++, neinštaluje sa, len spustí.




Riadok 199: Riadok 199:




Prijímanie znakov zabezpečí funkcia ReadFile, ktorá načítaný znak uloží do buffera Data (v našom príklade 1 byte). Zároveň vráti aj počet prijatých bytov. Táto funkcia čaká, až kým nejaký znak nepríde. Takže ak nepríde nič, program "zamrzne". Tomu sa dá vyhnúť použitím timeoutov, ktoré uvedieme ďalej.
Prijímanie znakov zabezpečí funkcia <code>ReadFile</code>, ktorá načítaný znak uloží do buffera <code>Data</code> (v našom príklade 1 byte). Zároveň vráti aj počet prijatých bytov. Táto funkcia čaká, až kým nejaký znak nepríde. Takže ak nepríde nič, program "zamrzne". Tomu sa dá vyhnúť použitím timeoutov, ktoré uvedieme ďalej.


V nasledujúcej ukážke sme navyše vyčistili prijímací a vysielací zásobník (buffer) funkciou PurgeComm.
V nasledujúcej ukážke sme navyše vyčistili prijímací a vysielací zásobník (buffer) funkciou <code>PurgeComm</code>.




Riadok 214: Riadok 214:
           &Data,        // Pointer na buffer, kam ukladáme prijaté dáta
           &Data,        // Pointer na buffer, kam ukladáme prijaté dáta
           1,            // Počet bytov, na ktoré čakáme
           1,            // Počet bytov, na ktoré čakáme
          &d,           // Pointer na skutočný počet prijatých dát
            
           &Pocet,        // Pointer na skutočný počet prijatých dát
           &Pocet,        // Pointer na skutočný počet prijatých dát
           NULL);        // Musí byť NULL
           NULL);        // Musí byť NULL
Riadok 227: Riadok 227:
   printf("\n OK, z portu sme uspesne prijali [%c].\n",Data);
   printf("\n OK, z portu sme uspesne prijali [%c].\n",Data);
</source>
</source>
Úloha: Napíšte program, ktorý bude na čakať na zvolenom porte a stále dokola zobrazovať všetky prijaté znaky. Overte funkciu vysielaním v bežnom terminálovom programe, ako je napr. Hyperterminál, alebo Terminal.




'''Úloha:''' Napíšte program, ktorý bude na čakať na zvolenom porte a stále dokola zobrazovať všetky prijaté znaky. Overte funkciu vysielaním v bežnom terminálovom programe, ako je napr. Hyperterminál, alebo Terminal.


=== '''3. 5. Timeouty'''
=== '''3. 5. Timeouty''' ===
===


Aby funkcia ReadFile nezostala čakajúca na znak, používajú sa tzv. timeouty, maximálne časy, ktoré má daná funkcia trvať. Konkrétne hodnoty sú uložené v štruktúre COMMTIMEOUTS, ktorá má tieto prvky:
Aby funkcia <code>ReadFile</code> nezostala čakajúca na znak, používajú sa tzv. timeouty, maximálne časy, ktoré má daná funkcia trvať. Konkrétne hodnoty sú uložené v štruktúre <code>COMMTIMEOUTS</code>, ktorá má tieto prvky:


   ReadIntervalTimeout        =  20;  Max. doba v [ms] medzi dvoma príchodzími znakmi
   ReadIntervalTimeout        =  20;  Max. doba v [ms] medzi dvoma príchodzími znakmi
Riadok 251: Riadok 249:
                                       sa ešte pripočíta táto hodnota v [ms].
                                       sa ešte pripočíta táto hodnota v [ms].


Ak je hodnota niektorého parametra 0, potom sa daný timeout nevyhodnotí. Ako vidno, máme vlastne dve hodnoty timeoutu. Prvá je IntervalTimeout, druhá je (TotalTimeoutConstant + TotalTimeoutMultiplier * Počet bytov) - aplikuje sa vždz tá hodnota, ktorá nastane skôr.
Ak je hodnota niektorého parametra 0, potom sa daný timeout nevyhodnotí. Ako vidno, máme vlastne dve hodnoty timeoutu. Prvá je <code>IntervalTimeout</code>, druhá je <code>(TotalTimeoutConstant + TotalTimeoutMultiplier * Počet bytov)</code> - aplikuje sa vždz tá hodnota, ktorá nastane skôr.


Po naplnení štruktúry vhodnými hodnotami sa ešte musia stať platnými pre daný komunikačný port, čo urobíme funkciou SetCommTimeouts.
Po naplnení štruktúry vhodnými hodnotami sa ešte musia stať platnými pre daný komunikačný port, čo urobíme funkciou <code>SetCommTimeouts</code>.


<source lang="c++" style="background: LightYellow;">
<source lang="c++" style="background: LightYellow;">

Aktuálna revízia z 12:57, 3. apríl 2021

Tretí blok cvičení: Sériové komunikačné rozhranie

Poslednú tretinu semestra budeme komunikovať. To preto, lebo komunikácia je neoddeliteľnou súčasťou súčasných počítačov. A aj preto, lebo doteraz sme riešili jednoduché, niekoľkoriadkové programy. Teraz je čas na rozsiahlejší projekt, v ktorom sa naučíte niečo nové ale aj využijete mnoho z predošlých zadaní.


Tento blok úloh na seba nadväzuje, takže výsledok jednej budete potrebovať v nasledujúcej. Preto odpúčame riešiť ich postupne. Na konci vášho snaženia by mal byť jednoduchý program ("talker"), ktorým si budete môcť písať s kamarátom (frajerkou) pri susednom počítači.


Už onedlho budete mať vďaka pochopeniu pracovníkov VS prepojené vždy dva susedné počítače cez sériovú linku, vždy na porte COM1. Prepojenie je realizované káblom typu tzv. null-modem.


Pozn: Dnes, 24. apríla 2006 by už mali byť všetky počítače prepojené.


Literatúra

Teóriu a popis sériového komunikačného rozhrania nájdete napr. tuto:

Na testovanie a prvé pokusy sa vám určite zíde aj nejaký terminálový program, napríklad tento

Tí, čo chcú pracovať doma, si môžu prepojiť dva porty káblom typu null-modem (prekrížený), alebo dokonca len na jednom porte prepojiť piny RxD a TxD. Ešte jednoduchšia alternatíva je nainštalovať tzv. virtuálny sériový port, resp. pár takýchto portov, ktoré sa prepoja tiež virtuálnym null-modem káblom. Tým v počítači vzniknú dva nové porty, s ktorými pracujete ako s klasickými.

Toto riešenie sa ale nedá použiť v CPU, pretože nemáte dostatočné oprávnenia na inštaláciu.

A pre tých, ktorí by potrebovali podrobnejšie preskúmať, ako porty fungujú, alebo nefungujú, odporúčame použiť monitor všetkej komunikácie PortMon. Oceníte hlavne vtedy, keď niečo nefunguje, alebo pri ladení zložitejších protokolov.


Programovanie

Úplný popis toho, ako sa programuje sériová linka pod Win32 nájdete na stránkach MSDN Microsoftu:

Pre záujemcov o hlbšie štúdium možno odporučiť aj knihu

  • Václav Vacek: Sériová komunikace ve Win32. Nakladatelství BEN, Praha, 2003.

Vo všeobecnosti možno povedať, že Win API poskytuje prístup k sériovému komunikačnému rozhraniu na podobnom princípe ako k súborom a ďalším sekvenčným zariadeniam. Základný postup je takýto:

  1. Port otvoriť
  2. Nastaviť komunikačné parametre a timeouty
  3. Zápis na port
  4. Čítanie z portu
  5. Zatvorenie portu


3.1 Otvorenie portu

Otvorenie portu má na starosti funkcia CreateFile, pričom sa tvárime, že náš port COM1: je existujúci súbor, do ktorého ideme normálne zapisovať, prípadne z neho aj čítať.

  HANDLE hCom;                            // Handle na objekt typu súbor
  
  hCom = CreateFile(                 
         "COM1:",                         // Názov portu, ktorý otvárame
         GENERIC_READ | GENERIC_WRITE,    // Otvárame na čítanie aj na zápis
         0,                               // Zdieľanie nebude žiadne
         NULL,                            // Bezpečnostné info žiadne
         OPEN_EXISTING,                   // Otvárame existujúci (port)
         0,                               // Žiadne atribúty súboru
         NULL);                           // Musí byť NULL

  if ( hCom == INVALID_HANDLE_VALUE )
     printf("\n Chyba:  Port sa neda otvorit.\n");
     // potom nasleduje    CloseHandle(hCom);  a potom asi exit...
  else
     printf("\n OK, port je uspesne otvoreny.\n");

3.2 Komunikačné parametre.

Dve zariadenia (počítače) budú spolu komunikovať len vtedy, ak budú mať oba nastavené rovnaké komunikačné parametre. Aktuálne nastavenie si môžete pozrieť tak, že v konzolovom okne napíšete príkaz

mode com1:

Dostanete výstup podobný tomu na obrázku, zobrazia sa najdôležitejšie komunikačné parametre:

Význam parametrov je nasledovný:


Baud
prenosová rýchlosť - z nej vyplýva dĺžka trvania jedného bitu. Aj keď teoreticky môže byť rýchlosť ľubovoľná, podľa medzinárodných noriem sa používajú len niektoré typizované rýchlosti (600, 1200, 2400, 4800, 9600, 19 200 Bd). Udáva sa rýchlosť v Bd (Baud), čo je bit za sekundu.
Parity
pre zvýšenie spoľahlivosti prenosu môžu byť údaje zabezpečené paritným bitom, ktorý je doplnený za prenášané bity tak, aby celkový počet jednotiek bol párny (parity even), alebo nepárny (parity odd). Prijímateľ správy si ľahko môže overiť, či pri prenose nedošlo k chybe. Neošetrí sa tým veľa chýb, ale je to často lepšie ako nič. V príklade na obrázku sa zabezpečenie prenosu paritou nepoužíva.
Data Bits
Počet prenášaných bitov. Napriek tomu, že sa zdá zrejmé, že to bude 8, nemusí to tak byť a bežne sa používa aj 7 (ak nám stačí prenášať len čistý ASCII text) a niekedy aj 9.
Stop Bits
Počet stop bitov. Stop bit býva najčastejšie len jeden, ale pri problémoch so synchronizáciou sa používajú aj dva, niekedy jeden a pol.
Timeouts
Uvádza, či je použité sledovanie timeoutov. V našom prípade áno.
CTS, DTR, RTS, ...
Ostatné parametre uvádzajú stav a parametre pre handshaking - potvrdzovanie pre zvýšenie spoľahlivosti prenosu. Nebudeme používať.


Význam jednotlivých parametrov je aj na nasledujúcom obrázku. Pripomíname, že podľa RS-232 je log. 0 (space) +5 až +15 V, log. 1 (mark) -5 až -15 V. Ak sa na linke nič nedeje (idle) je v stave log. 1, štart bit je vždy log. 0 (space) a stop bit naopak log. 1 (mark). Na obrázku je ako príklad uvedené vysielanie znaku 'U' (85, 0x55, 0101 01012). Všimnite si, že pri prenose protokolom RS-232 sa prenáša najnižší bit (LSB) ako prvý. Pri bežnom vysielaní údajov cez RS-232 po doplnení štart- a stop-bitu vyzerá signál takto:



Teoretický a nameraný priebeh signálov pri prenose znaku 'U'.

Horeuvedené parametre môžete zmeniť napríklad už spomenutým príkazom mode v konzolovom okne:

mode com1: 9600,n,8,1

Uvedený príkaz nastaví port COM1 na rýchlosť 9600 Baudov, bude sa prenášať vždy 8 bitov ukončených 1 stopbitom a nezabezpečené paritou.

Samozrejme, že parametre je možné (a vhodné) meniť aj priamo z vášho programu v jazyku C. Parametre sú uložené v štruktúre typu DCB, ktorej jednotlivé položky zodpovedajú jednotlivým parametrom komunikácie. Pomocou funkcie GetCommState naplníme štruktúru aktuálnymi parametrami, funkciou SetCommState zasa nastavíme komunikačné parametre podľa zodpovedajúcich položiek štruktúry. Samotný zápis do štruktúry DCB nikde nič nezmení.

/* *** V ďalšom predpokladáme, že port sme už úspešne otvorili...       *** */
 
 DCB PortDCB;                       // Štruktúra, v ktorej sú uložené parametre    
  
 PortDCB.DCBlength = sizeof(DCB);  // Inicializuj položku DCBlength
 GetCommState(hCom,&PortDCB);       // Načítaj aktuálne nastavenia
 printf("\nBaud rate:       %d",PortDCB.BaudRate); 
 ... 
 PortDCB.BaudRate = 9600;           // Zmeníme nejaký parameter v DCB
 ...
 int err;
 err = SetCommState(hCom,&PortDCB); // Nastav aktuálne nastavenia na port
 if(!err)   
     printf("Chyba:  Port sa neda nakonfigurovat.");
     // nasleduje CloseHandle(hCom);   a asi exit...
  else
     printf("OK, port je uspesne nakonfigurovany.");


Úloha: Preštudujte si na stránkach MSDN ako je vytvorená štruktúra "databázy" DCB (Structure Members) s parametrami sériového komunikačného rozhrania. Napíšte program (funkciu), ktorá vypíše obsah jednotlivých členov štruktúry. Nemusíte vypisovať všetko, stačí tie parametre, ktoré sme uviedli vyššie (rýchlosť, parita, počet bitov a stop bitov).

Tip: Výpis položiek urobte ako funkciu, zíde sa vám v ďalšej úlohe.

Úloha: Napíšte program, ktory zmení komunikačné parametre vybraného portu (COM1,COM2) na 9600,n,8,1. Dokumentujte výpisom DCB (viď predošlá úloha).

Tip: Aby ste mohli meniť parametre, musíte port najprv otvoriť.

3.3 Vysielanie

Zápis na port má na starosti funkcia WriteFile, pričom sa použijú aktuálne prenosové parametre. Funkcia uloží do premennej Pocet počet skutočne odvysielaných znakov, ktorý treba kontrolovať. V prípade zlyhania buď prenos zopakujeme, alebo skončíme s chybou.

Nasledujúca ukážka samozrejme predpokladá, že port je otvorený a správne nakonfigurovaný.


  DWORD Pocet=0;
  unsigned char Data='A';

  WriteFile(
            hCom,   // Handle portu, ktory ste otvorili CreateFile
            &Data,  // Smernik (pointer) na data, ktore chcem vysielat
            1,      // Pocet bytov, ktore chcem vyslat
            &Pocet, // Smernik (pointer) na pocet vyslanych dat
            NULL);  // Musi byt NULL

  if( Pocet != 1 ) // Ak pocet prenesenych dat nesuhlasi    
   printf("\n Chyba: Zapis na port sa nepodaril.\n");
     // a zasa nasleduje CloseHandle(hCom);  a potom exit...
  else
   printf("\n OK.");


Úloha: Napíšte program, ktorý bude na zvolený port stále dokola vysielať znak 'A'. Overte funkciu prijímaním v bežnom terminálovom programe.


Tip: Použite napr. Terminal by Bray++, neinštaluje sa, len spustí.


3. 4. Prijmi znak

Prijímanie znakov zabezpečí funkcia ReadFile, ktorá načítaný znak uloží do buffera Data (v našom príklade 1 byte). Zároveň vráti aj počet prijatých bytov. Táto funkcia čaká, až kým nejaký znak nepríde. Takže ak nepríde nič, program "zamrzne". Tomu sa dá vyhnúť použitím timeoutov, ktoré uvedieme ďalej.

V nasledujúcej ukážke sme navyše vyčistili prijímací a vysielací zásobník (buffer) funkciou PurgeComm.


  DWORD Pocet=0;            // Počet prijatých dát
  unsigned char Data;      // Sem ulož prijatý znak

  PurgeComm(hCom,PURGE_TXCLEAR | PURGE_RXCLEAR);

  ReadFile(
           hCom,         // Handle portu, z ktorého čítame
           &Data,        // Pointer na buffer, kam ukladáme prijaté dáta
           1,            // Počet bytov, na ktoré čakáme
          
           &Pocet,        // Pointer na skutočný počet prijatých dát
           NULL);        // Musí byť NULL

                         // Funkcia ReadFile tu teraz čaká, až kým niečo 
                         // nepríde, ale môže aj skončiť bez zachyteného znaku, 
                         // ak medzitým vypršia timeouty.

  if( Pocet != 1 )      // Ak počet prenesených dát nesúhlasí    
   printf("\n Chyba: Citanie z portu nedalo nic.\n");
  else
   printf("\n OK, z portu sme uspesne prijali [%c].\n",Data);


Úloha: Napíšte program, ktorý bude na čakať na zvolenom porte a stále dokola zobrazovať všetky prijaté znaky. Overte funkciu vysielaním v bežnom terminálovom programe, ako je napr. Hyperterminál, alebo Terminal.

3. 5. Timeouty

Aby funkcia ReadFile nezostala čakajúca na znak, používajú sa tzv. timeouty, maximálne časy, ktoré má daná funkcia trvať. Konkrétne hodnoty sú uložené v štruktúre COMMTIMEOUTS, ktorá má tieto prvky:

 ReadIntervalTimeout         =  20;  Max. doba v [ms] medzi dvoma príchodzími znakmi
                                     pri čítaní. Ak je táto doba prekročená, operácia
                                     čítania sa predčasne ukončí.
 ReadTotalTimeoutMultiplier  =  10;  Max. doba v [ms] ktorá sa vynásobí počtom
                                     prijímaných bytov a takto dá celkovú max.
                                     dobu prijímania.
 ReadTotalTimeoutConstant    = 100;  K predošlej hodnote (ReadTotalTimeoutMultiplier) 
                                     sa ešte pripočíta táto hodnota v [ms].
 WriteTotalTimeoutMultiplier =  10;  Max. doba v [ms] ktorá sa vynásobí počtom
                                     vysielaných bytov a takto dá celkovú max.
                                     dobu vysielania.
 WriteTotalTimeoutConstant    = 100;  K predošlej hodnote (WriteTotalTimeoutMultiplier) 
                                     sa ešte pripočíta táto hodnota v [ms].

Ak je hodnota niektorého parametra 0, potom sa daný timeout nevyhodnotí. Ako vidno, máme vlastne dve hodnoty timeoutu. Prvá je IntervalTimeout, druhá je (TotalTimeoutConstant + TotalTimeoutMultiplier * Počet bytov) - aplikuje sa vždz tá hodnota, ktorá nastane skôr.

Po naplnení štruktúry vhodnými hodnotami sa ešte musia stať platnými pre daný komunikačný port, čo urobíme funkciou SetCommTimeouts.

  COMMTIMEOUTS timeouts;
   
  timeouts.ReadIntervalTimeout         =  20;
  timeouts.ReadTotalTimeoutMultiplier  =  10;
  timeouts.ReadTotalTimeoutConstant    = 100;
  timeouts.WriteTotalTimeoutMultiplier =  10;
  timeouts.ReadTotalTimeoutConstant    = 100;
  
  if (!SetCommTimeouts(hCom,&timeouts))
   printf("\n Chyba: Timeouty nenastavene.\n");
     //  a asi exit...
  else
   printf("\n OK, nastavene Timeouty\n");


3. 6. Zatvor port

Po skončení práce so sériovou linkou je slušné uvoľniť ju pre ďalšie programy a zatvoriť port.

  CloseHandle(hCom);   // zatvorime port




Úloha -- projekt (12b.)

Naprogramujte jednoduchý terminálový program, pomocou ktorého si budete môcť písať so susedným počítačom prepojeným sériovým káblom. Program bude mať dve okienka na prijatý a odosielaný text, možnosti voľby prenosovej rýchlosti a portu, archiváciu do súboru.

Možné modifikácie: len jedno okno, ale s farebným odlíšením prijatého a odoslaného textu, konfigurácia zo súboru, posielanie a prenášanie súborov,

V princípe je možné zadanie splniť akýmkoľvek vlastným komunikačným programom, napr. komunikácia s mobilom cez virtuálny sériový port a rozhranie Bluetooth, alebo infračervený port, je možné komunikovať s jednočipovým mikropočítačom (Atmel AVR, Microchip PIC, Intel 8051, BasicStamp,...), prípadne riadiť robota (protokol na požiadanie poskytne Ing. Balogh) a pod.



Bodovanie (každá položka cca 1 bod, spolu max. 12 bodov)

  • Otvorenie portu
  • Výpis položiek DCB
  • Nastavenie parametrov, kontrola nastavenia, overenie
  • Odskúšanie komunikácie terminálom
  • Vysielanie znaku dokola, celého textu
  • Prijatie jedného znaku
  • Prijatie celého textu
  • Timeouty, ošetrenie chýb
  • Logovanie do súboru
  • Rozlíšenie farebné-priestorové
  • Možnosť konfigurácie
  • Písomná dokumetácia s popisom programu (rozsah 1 x A4).

Tip 1: Všetky úlohy budeme programovať ako konzolové aplikácie, ale pokročilejší študenti sa môžu pokúsiť aj o okienka. Tu nájdete video, ako naprogramovať jednoduché okno, môžete ho pouziť ako základ, na ktorom budete stavať.

   Inštruktážne video
   Zdrojáky pre tento projekt (c) Norbert Kornyi 

Tip 2: Komu sa sériová linka celkom protiví, môže to isté urobiť so socketmi. Teda komunikovať by mali dva počítače so zadanou IP adresou.

   Inšpiračné zdrojáky I (c) Norbert Kornyi
   Inšpiračné zdrojáky II (c) Pavel Petrovič