|
Asinhrona zaporedna komunikacija
Besedilo vaje: Izdelajte program, ki bo na asinhronih zaporednih vratih uart1 sprejemal znake in jih nespremenjene oddajal nazaj. Ob sprejemu kode Esc naj se sprejeti znaki, namesto da so oddani nazaj, pričnejo izpisovati na prikazovalniku LCD. Ob sprejemu naslednje kode Esc naj se zopet vzpostavi prvotno stanje.
Razlaga: Program napišete v razvojnem okolju, ki teče na osebnem računalniku. Pripravljen delovni prostor (angl. workspace) z vsemi pripadajočimi datotekami najdete tukaj. Opis datotek, ki se nahajajo v paketu, najdete v uvodu razlage prve vaje (samostojni zagon), datoteka vic.c pa je opisana v tretji vaji (časovnik). Dodani sta datoteki uart.c in uart.h, kjer je se nahaja koda za inicializacijo asinhronih zaporednih vrat. Lastno kodo, ki izvaja to vajo, dodate v prazno definicijo funkcije main() v datoteki main.c. Najprej morate mikrokrmilnik inicializirati, kar naredite s klicem funkcije init() v funkciji start_up() v datoteki startup.c. Za njeno uporabo morate vključiti datoteko init.h. Opis funkcije init() prav tako najdete v razlagi prve vaje (samostojni zagon). Terminalski program na osebnem računalniku bo odtipkane znake pošiljal na asinhrona zaporedna vrata (na primer COM1), sprejete znake pa bo prikazoval na zaslonu. Ker bo mikrokrmilniški sistem znake nespremenjene oddajal nazaj, bo terminalski program odtipkano prikazoval na zaslonu. Ob pritisku tipke Esc bo zaslon 'zamrznil', odtipkani znaki se bodo pričeli prikazovati na prikazovalniku LCD. Nov pritisk tipke Esc zopet vzpostavi prvotno stanje. Tako s tipko Esc pravzaprav izbiramo med zaslonom in prikazovalnikom LCD, kjer naj se odtipkani znaki prikazujejo. Mikrokrmilnik mora ves čas sprejemati podatke, ki prispejo preko asinhronih zaporednih vrat uart1, ter se nato odločiti, ali bo prispeli znak oddal nazaj, ali ga bo prikazal na prikazovalniku LCD. Sprejem je mogoče narediti na dva načina. V prvi različici programska oprema sama od časa do časa preveri, ali je prispel nov znak. Da prestrežemo vse prispele znake, mora biti preverjanje dovolj pogosto. Pri drugem načinu asinhrona zaporedna vrata s prekinitvijo opozorijo, da je prispel nov znak. Naredimo vajo najprej na prvi način. Asinhrona zaporedna vrata uart1 moramo pred uporabo inicializirati, kar naredimo s klicem funkcije uart1_init(), ki je že napisana v datoteki uart.c. Funkcijo uart1_init() kličemo iz funkcije start_up(), kjer so zbrane vse inicializacije. Za njeno uporabo morate vključiti datoteko uart.h. Deklaracija funkcije uart1_init() je naslednja: void uart1_init(int baud, int length, int stop, int parity, int type, int hpins, int interrupts); Argumenti funkcije podajajo nastavitve asinhronih zaporednih vrat uart1:
Nastavitve asinhronih zaporednih vrat uart1 (hitrost prenosa, dolžina podatka, število stop bitov in način preverjanja paritete) se morajo ujemati z nastavitvami terminalskega programa na osebnem računalniku. Protokola prenosa (angl. flow control ali handshake) ne moremo nastavljati avtomatsko. Vedno ga je potrebno implementirati v programski opremi, naj si gre za strojni (angl. hardware) protokol s pomočjo signalov rts in cts, oziroma dsr in dtr, ali programski protokol s pomočjo znakov Xon in Xoff. V naši vaji protokola prenosa ne bomo implementirali. To pomeni, da oddajnik podatek pošlje takoj, ne glede na to, ali je sprejemnik na sprejem pripravljen. V prvem načinu izvedbe te vaje asinhrona zaporedna vrata uart1 ne zahtevajo prekinitev, zato prekinitve onemogočimo. Algoritem podaja spodnja slika. Inicializacija mikrokrmilnika in asinhronih zaporednih vrat uart1 je izpuščena.
Prispeli znak preberemo iz registra U1RBR. Pred branjem preverimo, ali je podatek veljaven. To povesta bita rdr in rxfe v registru U1LSR. Bit rdr označuje, da nas v registru U1RBR čaka podatek, medtem ko se zastavica rxfe postavi, kadar je prišlo pri sprejemu do napake. V registru U1RBR imamo tako veljaven podatek, kadar je bit rdr postavljen, bit rxfe pa ne. Znak, ki ga hočemo oddati, zapišemo v register U1THR. Po inicializaciji oddaja ni omogočena. Omogočimo jo s postavitvijo bita txen v registru U1TER. Pri izpisu znakov na prikazovalnik LCD si pomagamo z gonilnikom lcd_driver_1(), katerega izvorna koda je priložena v datoteki main.c. Gonilnik ob vsakem klicu na prikazovalnik LCD izpiše vsebino niza, na katerega kaže globalni kazalec lcd_string (glej prvo vajo - samostojni zagon). Vajo naredimo še na drug, bolj pravilen način. Asinhrona zaporedna vrata uart1 z zahtevo po prekinitvi sama opozorijo, da je prispel nov znak. Mikrokrmilnik med čakanjem nanj ni polno zaposlen s preverjanjem kdaj bo znak prispel, kot je to primer zgoraj. V tem času lahko opravlja druga opravila. Asinhrona zaporedna vrata uart1 inicializiramo podobno kot prej, le da omogočimo zahtevo po rx_data_available prekinitvi. Prekinitev rx_data_available je zahtevana, ko prispe nov znak, ali ko prispeli znak po določenem času še vedno ni bil prebran in tako odstranjen iz registra U1RBR. Po inicializaciji oddaja ni omogočena, zato moramo enako kot zgoraj postaviti bit txen v registru U1TER. Da do prekinitve res pride mora biti vektorski nadzornik prekinitev pravilno nastavljen, kar naredimo s klicem funkcije vic_init(). Funkcija se nahaja v datoteki vic.c, njen opis pa najdete v razlagi tretje vaje (časovnik). Nastavimo ga tako, da zahteva uart1 prekinitev tudi dejansko sproži. V ta namen potrebujemo pripadajočo prekinitveno funkcijo, ki se bo izvršila ob vsakem prispelem znaku. Algoritem funkcije prikazuje slika spodaj. S tem je glavni program, ki pravzaprav vsebuje le nastavitve, zaključen. Končamo ga z neskončno zanko.
Kot je bilo povedano zgoraj, je zahteva po prekinitvi podana v dveh primerih. Za kateri primer gre lahko razberemo iz registra U1IIR. Nepostavljen bit interrupt_pending pove, da je bila zahteva po uart1 prekinitvi dejansko izdana. Z masko interrupt_id iz registra izločimo bite, ki povedo, za katero uart1 prekinitev gre. V našem primeru sta to prekinitvi rx_data_available_id in character_time_out_id. V prvem primeru imamo opravka z novim znakom, v drugem primeru pa z dalj časa neprebranim znakom. Drugi primer se pravzaprav nebi smel nikdar zgoditi, saj vsak prispeli znak takoj obdelamo. Vendar temu ni povsem tako. Lahko se zgodi, da imamo v sprejemnem registru U1RBR znak, ki je ostal še od prej. Z izvršitvijo prekinitvene funkcije je trenutna zahteva po uart1 prekinitvi obdelana. V našem primeru jo umaknemo s tem, ko preberemo sprejemni register U1RBR. | |||||||||||||||||||||||||||||||||||
![]() |
![]() |
![]() |