Mikrokontroler PIC16f628a in LCD prikazovalnik
LCD prikazovalniki (Liquid cristal display – prikazovalniki na tekoče kristale) omogočajo izpisovanje črk, številk in ostalih znakov. Uporabljamo jih kot vmesnike med uporabnikom in elektronsko napravo. V vajah bomo uporabili dvovrstične LCD prikazovalnike z lastno osvetlitvijo, ki imajo v vsaki vrstici po 16 znakov (DEM 16216 SYH-PY).
Slika 1: LCD prikazovalnik 2x16
Vsebujejo lasten kontroler (KS0070B-00 proizvajalca Samsung oziroma HD44780U proizvajalca Hitachi), ki skrbi za vklapljanje ustreznih pik na zaslonu. Vsak znak je sestavljen iz 5 x 8 pik. LCD kontroler ima 80 bajtov zaslonskega pomnilnika RAM, imenovanega DDRAM (Display data RAM). Vsebuje še znakovni pomnilnik ROM s podatki o simbolih (imenovanega CGROM) in 64 bajtov zaslonskega pomnilnika RAM (imenovanega CGRAM), namenjenega izdelavi lastnih simbolov. CGROM je pomnilnik, ki ga ne moremo spreminjati. Vsebuje podatke o tem, kako so sestavljeni znaki, ki jih LCD pozna: vse črke angleške abecede, številke, ločila in še ostale znake, ki so odvisni od proizvajalca, osnoven nabor znakov ASCII pa je vedno enak.
Slika 2: LCD 2x16, naslovi DDRAM - šestnajstiške vrednosti
Vsak znak na LCD prikazovalniku ima svoj naslov (slika 26) v DDRAM-u. Začetni naslov skrajnega levega znaka v prvi vrstici je 0x00, skrajnega levega znaka v drugi vrstici pa 0x40. Vsaka vrstica je navidezno dolga po 40 znakov, kar znaša skupaj 80 znakov. Vsak znak zavzame 1 bajt pomnilnika DDRAM. Od vseh 40 znakov v vsaki vrstici jih trenutno vidimo na zaslonu le 16. Tega lahko poljubno premikamo po celotnem prikazovalniku in tako določimo, kaj bo v določenem trenutku prikazano na zaslonu.
Slika 3: Kar v resnici vidimo na LCD zaslonu je le del celotnega DDRAM-a
CGRAM ima enako vlogo kot CGROM, le da lahko vanj sami vpisujemo podatke oziroma jih iz njega beremo. Če delamo z znaki velikosti 5 x 8 pik, lahko ustvarimo do 8 novih znakov, ki imajo kode od 0x00 do 0x07. Vsak znak vsebuje 8 naslovov, vrednosti znaka pa so odvisne od novega simbola oziroma znaka, ki smo ga ustvarili.
Priklop LCD prikazovalnika na mikrokontroler:
LCD prikazovalniki brez možnosti osvetlitve zaslona imajo 14 priključkov, taki z možnostjo osvetlitve zaslona pa imajo 2 priključka več, torej 16 priključkov. Funkcija posameznega priključka je standardna, vedeti moramo le, kje se nahaja prvi priključek. Če priključki za določen tip LCD-ja niso označeni, poiščemo podatke na spletu.
Slika 4: Priklop LCD prikazovalnika na mikrokontroler
Priključki od 7 do 14 LCD prikazovalnika so namenjeni podatkovnim linijam (od D0 do D7). Preko njih se prenašajo podatki od mikrokontrolerja do LCD-ja, če pišemo v LCD, in od LCD-ja do mikrokontrolerja, če beremo iz LCD-ja. LCD prikazovalniki lahko delujejo v 8 ali v 4-bitnem načinu. Mi bomo izbrali 4-bitni način delovanja, saj s tem privarčujemo pri priključkih mikrokontrolerja. Ker iz LCD-ja ne bomo ničesar brali, bomo priključek 5 (R/W) povezali z maso in s tem pridobili dodaten prost priključek na mikrokontrolerju. Podatki se pri 4-bitnem načinu delovanja prenašajo po 4 bite hkrati, zato moramo poslati obe polovici bajta posebej, pošiljamo pa jih po podatkovnih linijah od D4 do D7.
Tretji priključek LCD-ja služi nastavitvi kontrasta. Kontrast reguliramo z napetostjo na tem priključku. V ta namen uporabimo potenciometer kot delilnik napetosti.
LCD-prikazovalnik krmilimo tako, da mu pošiljamo ukaze ali podatke. Če postavimo priključek 4 (RS) v stanje logične 0, bo LCD sprejel podatek kot ukaz, če pa ga postavimo na nivo logične 1, bo LCD sprejel navaden podatek. Priključek 6 (E) je namenjen vklopu prikazovalnikove logike. Ko pošiljamo neki podatek na LCD, ga moramo nekako obvestiti, da je na podatkovnih linijah nov podatek. To storimo tako, da ta priključek vklopimo (postavimo na 1) in izklopimo (postavimo na 0). Ob tem prehodu bo LCD sprejel nov podatek. Pri 4-bitnem načinu delovanja pošljemo najprej zgornje 4 bite podatka na priključke 11–14 (D4–D7), vmes vklopimo in izklopimo priključek 6 (E) in nato na iste priključke pošljemo še spodnje 4 bite podatka. Zatem zopet vklopimo in izklopimo priključek 6 (E). Vsak vpis ukaza ali podatka vzame LCD-ju nekaj časa, da ga obdela. V tem času, podajajo ga proizvajalci LCD-jev, mu ne smemo pošiljati naslednjega ukaza ali podatka, zato uporabimo metodo zakasnitve, ki bo v našem primeru ca. 5 ms. Časa zakasnitve ni treba točno nastaviti, le manjši od predpisanega ne sme biti.
Tabela 1: Funkcije priključkov LCD prikazovalnika 2x16
LCD prikazovalnik moramo pred uporabo inicializirati. To pomeni, da moramo izvesti določeno zaporedje operacij, da ga postavimo v želeni način delovanja. Uporabili bomo inicializacijo za delo v 4-bitnem načinu. Opis korakov, potrebnih pri inicializaciji, si poglejmo v programu, ki bo v prvi vrstici LCD-ja zapisal "Mikrokontroler", v drugi vrstici pa "in LCD zaslon", kot kaže slika 5.
Slika 5: Prikaz napisa na LCD zaslonu
Program v zbirnem jeziku:
;-------------------------------------------------------------------------------------------------------------
; Prikaz napisa na LCD zaslonu.
; Okolje MPLAB IDE v8.92, prevajalnik MPASM Assembler V5.51, oscilator 4 MHz.
; Avtor: Milan Ivič, nov 2017
;-------------------------------------------------------------------------------------------------------------
list p=16f628a ;Tip mikrokontrolerja
#include <p16f628a.inc> ;Vključi v program datoteko p16f628a.inc.
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _LVP_OFF & _XT_OSC
;********************* Deklaracija spremenljivk *********************
Cblock 0x20 ;Blok konstant in začasnih spremenljivk
Temp1
Temp2 ;Začasni spremenljivki
Naslov ;Nov naslov DDRAM-a
Stevec ;Števec za izpis niza v prvi vrstici
Stevec1 ;Števec za izpis niza v drugi vrstici
endc
;******************* Definiranje novih oznak **********************
#define RS PORTB,1 ;RS-linija LCD-ja
#define RW PORTB,2 ;RW-linija LCD-ja
#define E PORTB,3 ;E-linija LCD-ja
;************** Definiranje konstant za LCD ukaze ***************
DDRAM equ 0x80 ;Maska za vpis v DDRAM
Kurzor equ 0x0C ;Izklop kazalčka (kurzorja)
;************** Začetek ***************
org 0x000
goto Glavni_zacetek
org 0x004
;********************* Glavni program *********************
Glavni_zacetek
bsf STATUS,5 ;Banka 1
clrf TRISA
clrf TRISB ;Vsi pini PORTA in PORTB so izhodni.
movlw b'10000001'
movwf OPTION_REG ;Preddelilnik dodeljen časovniku TMR0, preddelitev 1 : 4.
bcf STATUS,5 ;Banka 0
clrf PORTB ;Inicializacija PORTB
clrf INTCON ;Onemogočimo odzivanje mikrokontrolerja na prekinitve
call LCD_Init ;Klicanja podprograma za inicializacijo LCD-ja
bsf RS ;Podatkovni način, pisanje znakov
call LCD_Niz1 ;Klicanje podprograma za izpis niza na LCD (prva vrstica)
clrf Stevec ;Spremenljivka Stevec = 0
movlw 0x40
call DDRAM_n ;Pišemo na začetek druge vrstice LCD (naslov 0x40)
bsf RS ;Podatkovni način, pisanje znakov
call LCD_Niz2 ;Klicanje podprograma za izpis niza na LCD (druga vrstica)
clrf Stevec1 ;Spremenljivka Stevec1 = 0
Delaj goto Delaj ;Neskončna zanka
;********************* Podprogrami *************************
;******* Izpis na LCD v 4-bitnem načinu ********************
LCD_pisi
movwf Temp1 ;Podatek iz W v Temp1
movlw 0x0F ;b'00001111' v register W
andwf PORTB,f ;Zgornje 4 bite registra PORTB izbrišemo
movf Temp1,w ;Registru W povrnemo podatek iz Temp1
andlw 0xF0 ;Izbrišemo spodnje štiri bite
iorwf PORTB,f ;Pošljemo zgornje štiri bite podatka
bsf E
bcf E ;Obvestimo LCD o novem podatku
swapf Temp1,f ;Zamenjamo polbajte v Temp1
movlw 0xF0 ;b'11110000' v register W
andwf Temp1,f ;Spodnje 4 bite v Temp1 izbrišemo
movlw 0x0F ;b'00001111' v register W
andwf PORTB,f ;Zgornje 4 bite v registru PORTB izbrišemo
movf Temp1,w ;Podatek iz Temp1 v register W
iorwf PORTB,f ;Pošljemo spodnje štiri bite podatka
bsf E
bcf E ;Obvestimo LCD o novem podatku
call Cakaj_155_us ;Zakasnitev 155 µs
return
;****************** Inicializacija LCD-ja ********************
LCD_Init
bcf RW
bcf RS ;Ukazni način, vpisovanje podatkov v LCD
movlw .34
call Cakaj_ms ;ca. 35 ms pavze
movlw 0x30
movwf PORTB ;Na D7–D4 pošljemo 0x30
bsf E
bcf E ;Obvestimo LCD o novem podatku
movlw .5
call Cakaj_ms ;ca. 5 ms pavze
movlw 0x30
movwf PORTB ;Na D7–D4 pošljemo 0x30
bsf E
bcf E ;Obvestimo LCD o novem podatku
call Cakaj_155_us ;Zakasnitev 155 µs
movlw 0x30
movwf PORTB ;Na D7–D4 pošljemo 0x30
bsf E
bcf E ;Obvestimo LCD o novem podatku
movlw .5
call Cakaj_ms ;ca. 5 ms pavze
movlw 0x20
movwf PORTB ;Na D7–D4 pošljemo 0x20
bsf E
bcf E ;Obvestimo LCD o novem podatku
call Cakaj_155_us ;Zakasnitev 155 µs
;Sedaj smo v 4-bitnem načinu
movlw 0x28 ;LCD 2x16, 4-bitni način, velikost znakov 5 x 8 pik
call LCD_pisi ;Pošljemo 0x28 na LCD
movlw 0x08 ;Izklop zaslona
call LCD_pisi ;Pošljemo 0x08 na LCD
movlw 0x01 ;Brisanje DDRAM-a
call LCD_pisi ;Pošljemo 0x01 na LCD, izbrišemo zaslon
movlw .2
call Cakaj_ms ;Zahtevana zakasnitev, večja od 2 ms
movlw 0x06 ;Pisanje v desno brez pomikanja zaslona
call LCD_pisi ;Pošljemo 0x06 na LCD
movlw 0x0F ;Vklop zaslona, veliki kazalček (kurzor)
call LCD_pisi ;Pošljemo 0x0F na LCD
return
;****************** Nastavitev trenutnega naslova DDRAM-a ********************
;Naslov DDRAM-a vpišemo v register W pred klicem podprograma.
DDRAM_n
bcf RS ;Ukazni način
iorlw DDRAM ;Z OR-operacijo ustvarimo ukaz
call LCD_pisi ;Vpis ukaza v LCD
bsf RS ;Podatkovni način
return
;****************** Zakasnitev 155 µs ********************
Cakaj_155_us
movlw .50 ;Število ponovitev zanke prestavimo v register W
movwf Temp2 ;Vrednost registra W v Temp2
Se decfsz Temp2,f ;Temp2 = Temp2 - 1. Temp2 = 0?
goto Se ;Temp2 še ni enak 0. Še zmanjšuj Temp2
return ;Temp2 = 0. Vrni se iz podprograma
;********* Zakasnitev ca. 1 ms krat vrednost registra W *********
Cakaj_ms
movwf Temp2 ;W v Temp2 (iz LCD_Init je vrednost W enaka 34 oziroma 5)
clrf TMR0 ;Inicializacija registra TMR0
Cak1 btfss INTCON,T0IF ;Ali je TMR0 prekoračil svojo vrednost (255)?
goto Cak1 ;Ne še
bcf INTCON,T0IF ;Da, postavi zastavico T0IF na 0
decfsz Temp2,f ;Temp2 = Temp2 - 1
goto Cak1 ;Temp2 še ni enak 0
return ;Temp2 = 0. Vrni se iz podprograma
;*************** Naša niza, napisana sta v tabelah ***************
Tabela1
addwf PCL,f
DT "Mikrokontroler" ;Tabela1 z nizom
Tabela2
addwf PCL,f
DT "in LCD zaslon" ;Tabela2 z nizom
;*************** Niz1 za izpis v prvi vrstici LCD-ja ***************
LCD_Niz1
clrf Naslov ;Naslov = 0
bsf RS ;Podatkovni način
movlw .14
movwf Stevec ;Niz 1 ima 14 znakov (14 črk: Mikrokontroler)
Delaj1
movf Naslov,w ;Odmik = 0, začnemo s prvim znakom niza iz tabele 1
call Tabela1
call LCD_pisi ;Podatek iz tabele 1 v LCD
incf Naslov,f ;Naslednji podatek iz tabele 1
decfsz Stevec,f ;Ali se vpisani vsi podatki iz tabele 1 v LCD?
goto Delaj1 ;Ne še
bcf RS ;Da, preklopi na ukazni način
return
;*************** Niz2 za izpis v drugi vrstici LCD-ja ***************
LCD_Niz2
clrf Naslov ;Naslov = 0
bsf RS ;Podatkovni način
movlw .13
movwf Stevec1 ;Niz 2 ima 13 znakov (11 črk in 2 presledka: in LCD zaslon)
Delaj2
movf Naslov,w ;Odmik = 0, začnemo s prvim znakom niza iz tabele 1
call Tabela2
call LCD_pisi ;Podatek iz tabele 1 v LCD
incf Naslov,f ;Naslednji podatek iz tabele 1
decfsz Stevec1,f ;Ali se vpisani vsi podatki iz tabele 1 v LCD?
goto Delaj2 ;Ne še
bcf RS ;Da, preklopi na ukazni način
movlw Kurzor ;Izklopimo kazalček - kurzor (koda ukaza 0x0C v register W)
call LCD_pisi
return
end
Na začetku programa najdemo novo direktivo Cblock. Ta direktiva počne isto kot equ, le da nam nekoliko olajša delo. Z njo damo besedam vrednosti, ki so lahko naslovi spremenljivk. Prvi parameter poleg direktive (0x20) pomeni vrednost, ki jo bo imela prva oznaka na seznamu, ki tej direktivi sledi. V našem primeru je prva oznaka na seznamu Temp1, ki ima torej vrednost 0x20. To vrednost smo določili zato, ker se na tem naslovu prične podatkovni pomnilnik RAM, namenjen splošni uporabi pri mikrokontrolerju PIC16f628a. Vsaka naslednja oznaka na seznamu bo dobila vrednost, za 1 večjo od vrednosti predhodne oznake v seznamu.
Naslednja nova direktiva v programu je #define. Ta direktiva tekst v prvem parametru zamenja s tekstom v drugem parametru. Kadarkoli se npr. v programu pojavi beseda E, jo prevajalnik zamenja s tekstom PORTB,3, kamor tudi povežemo priključek 6 LCD-ja (E) na mikrokontroler (priključek RB3).
V podprogramu za inicializacijo LCD-ja najprej poskrbimo za zakasnitev 35 ms, da se lahko LCD-jeva elektronika stabilizira. Nato sledi natančno določeno zaporedje korakov, ki so potrebni za inicializacijo in delo v 4-bitnem načinu. Ti koraki so zapisani in komentirani v podprogramu LCD_Init.
V glavnem programu smo časovniku TMR0 dodelili preddelilnik 1 : 4, zato preseže svojo vrednost 255 v času ca. 1 ms pri 4 MHz oscilatorju. S TMR0 v podprogramu Cakaj_ms odmerimo zakasnitev, ki je odvisna od vrednosti delovnega registra W, preden se začne podprogram izvajati. Prvič je vrednost W enaka 34, drugič pa 5. V prvem primeru TMR0 34-krat preseže vrednost 255, v drugem primeru pa 5-krat, preden mikrokontroler zapusti podprogram. Da se pri prekoračenju TMR0 ne sproži prekinitev, smo registru INTCON izklopili njegov sedmi bit GIE. Zakasnitev 155 μs smo dosegli z vrtenjem programa v zanki v podprogramu Cakaj_155_us.
Poglejmo vlogo podprograma LCD_pisi bolj natančno. Podatek za vpis na LCD podprogram prebere iz registra W. Register W se namreč iz obeh tabel vsakič vrne z vrednostjo odmika, ki je v našem primeru črka ali presledek, in ga začasno shrani v Temp1. Kakšen bo odmik, je odvisno od vrednosti spremenljivke Naslov. Če je njegova trenutna vrednost 5, se bo register W iz tabele vrnil z vrednostjo šestega znaka v nizu. Spremenljivki Stevec in Stevec1 štejeta odmike v nizih, ki sta zapisana v obeh tabelah in skrbita, da tabele ne prekoračimo. Preden smo začeli iz posamezne tabele brati, smo postavili vrednost spremenljivke Naslov na 0, spremenljivkama Stevec in Stevec1 pa smo določili vrednost 14 oziroma 13, saj je v prvi tabeli 14 znakov (šteje tudi presledek), v drugi pa 13. V tem podprogramu smo uporabili nove instrukcije. Instrukcija andlw napravi logično operacijo AND med posameznimi enakoležečimi biti registra W in konstanto, podano v parametru, ki je v našem primeru 0xF0. Rezultat operacije se vpiše nazaj v register W. Ker imajo zgornji štirje biti vrednost 1 (F0 šestnajstiško je 11110000 dvojiško), instrukcija andlw pobriše priključke RB4–RB7, da se lahko nanje vpiše nov podatek, hkrati pa ohrani vrednosti priključkov RB0 – RB3, kamor so povezani RS, R/W in E priključki LCD-ja. Instrukcija andwf napravi v našem programu logično operacijo AND med registroma W in PORTB, rezultat operacije pa shrani nazaj v register PORTB. Instrukcija iorwf pa napravi logično operacijo OR med registrom W in registrom, zapisanim v prvem parametru, v našem primeru je to PORTB. Rezultat operacije shrani v register PORTB. Instrukcijo swapf že poznamo, v programu pa jo uporabimo zato, da v začasni spremenljivki Temp1, kjer je shranjena trenutna koda znaka iz tabele, zamenja spodnje 4 bite z zgornjimi 4 biti. To storimo zato, da pošljemo na LCD enkrat zgornje 4 bite podatka in drugič spodnje 4 bite, saj delamo v 4-bitnem načinu.
Poglejmo, kakšen je postopek vpisa črke M, ki je prvi znak v tabeli 1. ASCII koda znaka M v CGROM-u je 01001101(2). S to vrednostjo se iz tabele 1 vrne register W in jo shrani v Temp1. Nato registru W določimo vrednost 00001111(2) (0Fh), da lahko z instrukcijo andwf pobrišemo zgornje 4 bite registra PORTB. Zatem registru W povrnemo kodo znaka M, ki je bila shranjena v Temp1. Z masko 11110000(2) (F0h) in instrukcijo andlw pobrišemo spodnje 4 bite kode znaka M, ki ima sedaj vrednost 01000000(2). Da pri prenosu tega podatka na PORTB ne spremenimo spodnjih 4 bitov registra PORTB, uporabimo instrukcijo iorwf. Na podatkovnih linijah od D4 do D7 LCD-ja (povezani so na RB4 do RB7) so sedaj pripravljeni zgornji štirje biti kode znaka M. LCD moramo samo še obvestiti, da je pripravljen nov podatek, zato vklopimo in izklopimo njegov priključek E. Po tem prehodu je LCD podatek prejel. Poslati mu moramo še spodnje 4 bite kode znaka M. Z instrukcijo swapf zamenjamo polbajta v Temp1. Ta ima sedaj vrednost 11010100(2). Spodnji 4 biti kode znaka M so zamenjali mesto z zgornjimi 4 biti. Postopek enako kot prej ponovimo in po prehodu priključka E z 1 na 0 prejme LCD drugo polovico kode znaka M. Pred pošiljanjem drugega znaka iz tabele na LCD počakamo ca. 155 μs. Zakasnitve, ki jih moramo upoštevati, so za posamezne tipe LCD-jev različne. Najdemo jih v proizvajalčevih katalogih (priložena spodaj).
V katalogu so opisani tudi ukazi LCD prikazovalnika in potrebne zakasnitve, ki jih posamezni ukaz potrebuje, da ga LCD obdela. Vseh je namreč preveč, da bi jih obravnavali v tem gradivu. Vsak ukaz je koda dolžine enega bajta, ki jo pošljemo LCD-ju. Mednje sodijo:
Clear Display: Počisti zaslon in postavi DDRAM-a na 0x00.
Return Home: Pomakne vidni del zaslona na začetni položaj, DDRAM se ne spremeni.
Entry Mode Set: Znake lahko izpisujemo od leve proti desni ali nasprotno, vključimo ali izključimo lahko avtomatsko pomikanje vidnega polja zaslona.
Display ON/OFF Control: Omogoča izklop LCD-ja. Izbiramo lahko tudi obliko kazalčka (kurzorja) in ali bo viden ali ne.
Cursor or Display Shift: Vidni del zaslona pomika po DDRAM-u. Izbiramo lahko med desnim in levim pomikom. Omogoča tudi premikanje kazalčka.
Function Set: Izbiramo lahko med 4- in 8-bitnim delovanjem LCD-ja, število vrstic na LCD-ju in velikost znakov.
Set CGRAM Address: Nastavimo naslov CGRAM-a, od katerega dalje se bodo vpisovali poslani podatki.
Set DDRAM Address: Nastavimo naslov DDRAM-a, od katerega dalje se bodo vpisovali poslani podatki.
Read Busy Flag & Address: Vsak vpis ukaza ali podatka vzame LCD-ju nekaj časa, da ga obdela. S stanjem zastavice ugotovimo, ali je LCD zaključil delo.
V katalogu najdemo tudi naslove vseh znakov DDRAM-a, ki jih je LCD zmožen prikazati. Poleg teh pa lahko sami izdelamo do osem svojih znakov. Katere vrednosti in naslove CGRAM-a imajo, je prikazano v katalogu.
Nekateri ukazi, ki jih moramo poslati LCD prikazovalniku za posamezne naloge:
Tabela 2: Ukazi za LCD zaslon
Program za prikaz enakega napisa na LCD zaslonu v programskem jeziku c:
V program bomo vključili zaglavno datoteko, header file. Za programiranje mikrokontrolerja je splošen pristop ta, da deklariramo in definiramo funkcije v isti datoteki, v datoteki kjer je zapisana glavna funkcija void main(). Ta pristop je splošno sprejet, saj je programerju enostavno razumeti celoten program, napisan v eni datoteki. Zna pa povzročati probleme pri razumevanju programa, če je vključenih več zunanjih enot. Predstavljajmo si aplikacijo, ki vključuje LCD zaslon, matrično tipkovnico, GSM in RFID modul, ki so povezani z mikrokontrolerjem. Vse navedene enote potrebujejo posebne funkcije za delovanje in komunikacijo z mikrokontrolerjem. Če bi napisali vse te funkcije v isti datoteki, bi bil takšen program težko razumljiv in bi programerju povzročal težave pri njegovem razumevanju. Zato lahko ugotovimo, da moramo za večje aplikacije nekaj storiti, uporabiti modularno programiranje. To je pristop, v katerem so funkcije zapisane v drugi datoteki, kličemo pa jih iz glavnega programa z vpisom #include"ustrezna_datoteka.h". Če uporabljamo vse zgoraj opisane zunanje enote in uporabo zaglavnih datotek, bi namesto velikega števila vrstic in funkcij v glavni datoteki, iz nje le vključili klic posameznih:
#include"lcd.h"
#include"gsm.h"
#include"keypad.h"
#include"rfid.h"
Takšen pristop vsekakor zmanjša zapletenost programa, posamezne zaglavne datoteke pa lahko uporabljamo v kateremkoli programu.
Enako kot datoteko v kateri je zapisana glavna funkcija void main(), v projekt vstavimo tudi zaglavno (Header File) File >> New, jo poimenujemo in dodamo končnico .h. Shranjena mora biti v mapi projekta. V našem primeru jo bomo poimenovali lcd.h:
Slika 6: Projekt z vključeno zaglavno datoteko lcd.h
Program za prikaz napisa na LCD zaslonu v c jeziku:
/*
Prikaz napisa na LCD zaslonu.
Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.
Avtor: Milan Ivič, nov. 2017.
*/
#include <htc.h>
#include <pic.h>
//Definiranje priključnih pinov za LCD:
#define RS RB1
#define EN RB3
#define D4 RB4
#define D5 RB5
#define D6 RB6
#define D7 RB7
#define _XTAL_FREQ 4000000
__CONFIG (0x2129);
#include "lcd.h" //Vključitev header (zaglavne) datoteke lcd.h
void main()
{
int i;
TRISB = 0x00; //Pini RB0 - RB7 so izhodni
LCD4bitni_Init(); //Klicanje funkcije za inicializacijo LCD-ja
while(1)
{
LCD4bitni_Set_Cursor(1,0); //Prva vrstica prvi znak (znak 0)
LCD4bitni_Pisi_niz("Mikrokontroler");
LCD4bitni_Set_Cursor(2,0); //Druga vrstica prvi znak (znak 0)
LCD4bitni_Pisi_niz("in LCD zaslon");
}
}
Zaglavna datoteka (Header File) lcd.h:
//LCD 4-bitni način delovanja
void LCD4bitni_Port(char a) //Nastavitev bitov podatkovnih linij
{
if(a & 1) //& => IN operacija nad biti (1 dec = 0001 dvojiško)
D4 = 1; //Podatkovna linija D4
else
D4 = 0;
if(a & 2) //2 dec = 0010 dvojiško
D5 = 1;
else
D5 = 0;
if(a & 4) //4 dec = 0100 dvojiško
D6 = 1;
else
D6 = 0;
if(a & 8) //8 dec = 1000 dvojiško
D7 = 1;
else
D7 = 0;
}
void LCD4bitni_Cmd(char a)
{
RS = 0; //RS = 0, Ukazni način, vpisovanje podatkov v LCD
LCD4bitni_Port(a);
EN = 1; //E = 1, na pin RB3 pošlje 1
__delay_ms(4);
EN = 0; //E = 0, na pin RB3 po 4 ms pošlje 0. Obvestimo LCD o novem podatku
}
LCD4bitni_Clear()
{
LCD4bitni_Cmd(0x00); //Počisti zaslon LCD-ja
LCD4bitni_Cmd(0x01); //Postavi kurzor na prvo pozicijo
}
void LCD4bitni_Set_Cursor(char a, char b)
{
char temp,z,y;
if(a == 1) //Prva vrstica LCD-ja
{
temp = 0x80 + b;
z = temp>>4; //Spodnji štirje biti 8-bitnega podatka
y = (0x80+b) & 0x0F; //Zgornji štirje biti, IN operacija nad ustreznimi biti
LCD4bitni_Cmd(z); //Nastavitev vrstice
LCD4bitni_Cmd(y); //Nastavitev kolone
}
else if(a == 2) //Druga vrstica LCD-ja
{
temp = 0xC0 + b;
z = temp>>4;
y = (0xC0+b) & 0x0F;
LCD4bitni_Cmd(z);
LCD4bitni_Cmd(y);
}
}
void LCD4bitni_Init() //Inicializacija LCD-ja
{
LCD4bitni_Port(0x00);
__delay_ms(20);
LCD4bitni_Cmd(0x03); //4-bitni način
__delay_ms(5);
LCD4bitni_Cmd(0x03);
__delay_ms(11);
LCD4bitni_Cmd(0x03);
LCD4bitni_Cmd(0x02); //Vrne se domov (zaslon pomakne v prvotni položaj)
LCD4bitni_Cmd(0x02);
LCD4bitni_Cmd(0x08); //Izbira prve vrstice
LCD4bitni_Cmd(0x00); //Počisti prvo vrstico
LCD4bitni_Cmd(0x0C); //Izbira druge vrstice
LCD4bitni_Cmd(0x00); //Počisti drugo vrstico
LCD4bitni_Cmd(0x06); //Nastavi kazalček (kurzor) za premikanje v desno
}
void LCD4bitni_Pisi_znak(char a) //Funkcija za pisanje znakov (pošiljanje 8-bitnega podatka v 4-bitnem načinu)
{
char temp,y;
temp = a&0x0F; //Spodnji 4 biti
y = a&0xF0; //Zgornji 4 biti
RS = 1; //RS = 1 => Podatkovni način, pisanje znakov
LCD4bitni_Port(y>>4); //Pošiljanje podatkov
EN = 1;
__delay_ms(5);
EN = 0;
LCD4bitni_Port(temp);
EN = 1;
__delay_ms(5);
EN = 0;
}
void LCD4bitni_Pisi_niz(char *a) //Funkcija za pisanje nizov
{
int i;
for(i=0; a[i]!='\0'; i++)
LCD4bitni_Pisi_znak(a[i]);
}
void LCD4bitni_Pomakni_Desno() //Funkcija za pomikanje v desno
{
LCD4bitni_Cmd(0x01);
LCD4bitni_Cmd(0x0C);
}
void LCD4bitni_Pomakni_Levo() //Funkcija za pomikanje napisa levo
{
LCD4bitni_Cmd(0x01);
LCD4bitni_Cmd(0x08);
}
void LCD4bitni_Pisi_Znake(char a) //Pošiljanje 8-bitov po 4-bitnem načinu
{
char Spodnji_biti,Zgornji_biti;
Spodnji_biti = a&0x0F;
Zgornji_biti = a&0xF0;
RS = 1;
LCD4bitni_Port(Zgornji_biti>>4); //Pošiljanje zgornjih 4 bitov
EN = 1;
for(int j=2130483; j<=0; j--) NOP();
EN = 0;
LCD4bitni_Port(Spodnji_biti); //Pošiljanje spodnjih 4 bitov
EN = 1;
for(int j=2130483; j<=0; j--) NOP();
EN = 0;
}
void LCD4bitni_Pisi_Niz(char *a)
{
int j;
for(j=0;a[j]!='\0';j++)
LCD4bitni_Pisi_Znake(a[j]);
}
Izdelajmo sedaj program, ki bo uporabljal različne načine prikazovanja napisa na LCD zaslonu, pomikanje zapisa v levo in v desno ter prikazal osem znakov, ki smo jih sami ustvarili. Zaglavna datoteka lcd.h je enaka, napisali bomo le novi glavni program. Izpisi na LCD-ju naj bodo takšni, kot prikazuje spodnji posnetek:
V zaslonski pomnilnik CGRAM lahko podatke zapisujemo in iz njega podatke beremo. Njegova velikost je 64 bajtov, namenjen pa je izdelavi lastnih znakov oziroma simbolov. Če delamo z znaki velikosti 5 x 8 pik, lahko ustvarimo do 8 novih znakov. Vsak znak vsebuje 8 naslovov, vrednosti znaka pa so odvisne od simbola oziroma znaka, ki smo ga ustvarili. Mi smo ustvarili 8 znakov:
Slika 7: Kreiranje lastnih znakov in njihove vrednosti
Program v c jeziku:
/*
Prikaz napisa na LCD zaslonu. Pomikanje napisa, napisi znakov in lastno izdelanih znakov.
Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.
Avtor: Milan Ivič, nov. 2017.
*/
#include <htc.h>
#include <pic.h>
//Definiranje priključnih pinov za LCD:
#define RS RB1
#define EN RB3
#define D4 RB4
#define D5 RB5
#define D6 RB6
#define D7 RB7
#define _XTAL_FREQ 4000000
__CONFIG (0x2129);
#include "lcd.h" //Vključitev header (zaglavne) datoteke lcd.h
const unsigned short Posebni_znaki5x8[] = {
0b01110,0b01110,0b00100,0b01110,0b10101,0b00100,0b01010,0b01010, //Koda za CGRAM, posebni znak 1
0b00000,0b00000,0b01010,0b00100,0b00100,0b10001,0b01110,0b00000, //Koda za CGRAM, posebni znak 2
0b01010,0b10101,0b01010,0b10101,0b01010,0b10101,0b01010,0b10101, //Koda za CGRAM, posebni znak 3
0b00000,0b00100,0b00010,0b11111,0b00010,0b00100,0b00000,0b00000, //Koda za CGRAM, posebni znak 4
0b00000,0b01010,0b10101,0b10001,0b10001,0b01010,0b00100,0b00000, //Koda za CGRAM, posebni znak 5
0b11011,0b00000,0b01010,0b00000,0b10101,0b10001,0b01110,0b00000, //Koda za CGRAM, posebni znak 6
0b00100,0b01110,0b00100,0b00100,0b00100,0b01110,0b11111,0b11111, //Koda za CGRAM, posebni znak 7
0b01110,0b10001,0b01110,0b00100,0b11111,0b00100,0b01010,0b10001 //Koda za CGRAM, posebni znak 8
};
void main()
{
int i;
unsigned int j; char a;
TRISB = 0x00; //Pini RB0 - RB7 so izhodni
LCD4bitni_Init(); //Klicanje funkcije za inicializacijo LCD-ja
LCD4bitni_Cmd(0x04); //Naslov CGRAM
LCD4bitni_Cmd(0x00);
for (j = 0; j <= 63 ; j++)
{
LCD4bitni_Pisi_Znake(Posebni_znaki5x8[j]);
}
LCD4bitni_Cmd(0x00); //Počisti zaslon LCD-ja
LCD4bitni_Cmd(0x02); //Pomakni zaslon na začetek
while(1)
{
LCD4bitni_Set_Cursor(1,0); //Prva vrstica prvi znak (znak 0)
LCD4bitni_Pisi_niz("Mikrokontroler in LCD zaslon");
for(i=0; i<12; i++)
{
__delay_ms(400);
LCD4bitni_Pomakni_Levo();
}
__delay_ms(1000);
for(i=0; i<12; i++)
{
__delay_ms(600);
LCD4bitni_Pomakni_Desno();
}
__delay_ms(800);
LCD4bitni_Clear();
LCD4bitni_Set_Cursor(2,6);
LCD4bitni_Pisi_znak('M');
__delay_ms(500);
LCD4bitni_Set_Cursor(2,8);
LCD4bitni_Pisi_Znake(0b00100110); //Binarna vrednost za znak & (katalog)
__delay_ms(500);
LCD4bitni_Set_Cursor(2,10);
LCD4bitni_Pisi_znak('I');
__delay_ms(2000);
LCD4bitni_Clear();
__delay_ms(1000);
//Izpis posebnih znakov:
LCD4bitni_Set_Cursor(1,0);
LCD4bitni_Pisi_znak(0);
LCD4bitni_Set_Cursor(1,2);
LCD4bitni_Pisi_znak(1);
LCD4bitni_Set_Cursor(1,4);
LCD4bitni_Pisi_znak(2);
LCD4bitni_Set_Cursor(1,6);
LCD4bitni_Pisi_znak(3);
LCD4bitni_Set_Cursor(1,8);
LCD4bitni_Pisi_znak(4);
LCD4bitni_Set_Cursor(1,10);
LCD4bitni_Pisi_znak(5);
LCD4bitni_Set_Cursor(1,12);
LCD4bitni_Pisi_znak(6);
LCD4bitni_Set_Cursor(1,14);
LCD4bitni_Pisi_znak(7);
__delay_ms(2500);
LCD4bitni_Clear();
}
}