[PIC programování] – 9. díl
Vítám vás u dalšího dílu PIC programování.
V tomto díle se podíváme na to, jak zobrazit číslici na 7-segmentovém displeji, abychom v pozdějších dílech mohli zobrazit data přijatá na sériových sběrnicích.
Nejprve uvedu schéma zapojení a vysvětlím, jak 7-segmentový displej funguje a jak jej budeme řídit. Pokud již toto znáte, můžete následující část článku přeskočit.
Pojďme se nyní zaměřit na samotný displej. Jeho vnitřní schéma vypadá takto:
Na obrázku vlevo můžete vidět, kam jsou jednotlivé vývody displeje zapojeny do PIC mikrokontroléru (dále jen MCU – Microcontroller Unit). Displej se skládá z osmi LEDek, kde sedm z nich rozsvěcí sedm segmentů číslice a jedna rozsvěcí tečku. Všechny LEDky mají společnou anodu, tzn. pokud vývody A0-A2, C0-C4 připojíme ke groundu (LATxy = 0), budou dané segmenty svítit.
A to je celá magie…
Abychom mohli rozumně zobrazovat čísla např. z pracovního registru (WREG) v assembleru, musíme si nejprve udělat podprogram, který nám číslo převede na segmenty.
Řekněme, že máme číslo 5. Pak rozsvícené segmenty budou a, c, d, f, g (případně tečka – dp). To odpovídá bitům LATA<2>, LATC<1>, LATC<3>, LATA<1>, LATA<0>, případně LATC<2>, na MCU.
To odpovídá těmto hodnotám registrů LATx:
LATA = b’?????000′
LATC = b’???10×01′
kde ? je libovolná hodnota (tyto piny na MCU nepoužíváme), x je volitelná hodnota pro tečku.
Všimněme si, že celkový počet námi použitých bitů je 8. Toho můžeme využít a ukládat celé nastavení předchozích dvou LATx registrů do jednoho registru. Tedy náš podprogram bude jako vstup přijímat číslo od 0 – 15 (to je jedna číslice v hexadecimální soustavě) a jako výstup poskytne jeden registr s nastavením registrů LATx.
Formát tohoto výstupu bude následující: Bity LATA<0:2> budeme ukládat ve spodních třech bitech, bity LATC<0:4> budeme ukládat v horních pěti bitech. Tedy výstup bude vypadat takto: C4:C3:C2:C1:C0:A2:A1:A0.
Pro všechny možnosti budeme muset sestavit tabulku:
<Vstup>: <Výstup>
0x0: b’00100001‘
0x1: b’11100111‘
0x2: b’00110010‘
0x3: b’10100010‘
0x4: b’11100100‘
0x5: b’10101000‘
0x6: b’00101000‘
0x7: b’11100011‘
0x8: b’00100000‘
0x9: b’10100000‘
0xa: b’01100000‘
0xb: b’00101100‘
0xc: b’00111001‘
0xd: b’00100110‘
0xe: b’00111000‘
0xf: b’01111000‘
Pozn.: V tabulce výše je tečka vždy zhasnutá.
Pro takovéto tabulky máme v assembleru šikovný nástroj. Příkaz brw je skoková instrukce, která načte hodnotu pracovního registru (WREG) a skočí o stejný počet instrukcí dopředu.
tedy pokud WREG = 0x04, pak se po zavolání brw přeskočí následující čtyři instrukce a provede se až pátá.
Ukázka:
(WREG = 0x02)
1 2 3 4 5 |
brw bcf LATA,1 bsf LATA,1 bcf LATC,0 bsf LATC,0 |
MCU přečte hodnotu WREG, a provede instrukci bcf LATC,0. Pokračuje prováděním instrukce bsf LATC,0…
V minulém díle jsme použili instrukci “return” pro návrat z podprogramu. Dnes použijeme “retlw …”, což je obdoba return s tím rozdílem, že vracíme konstantu v registru W (dosadíme za …).
Tedy místo, abychom psali např.
1 2 |
movlw 0x20 return |
napíšeme pouze
1 |
retlw 0x20 |
V naší rutině musíme zajistit, aby program neskočil o neošetřený počet instrukcí. Pokud bychom měli jen 4 možnosti a v registru W bylo číslo 5, mohli bychom se dostat do potíží. Z tohoto důvodu použijeme logickou operaci AND (často označována symbolem &), abychom odřízli nežádoucí bity. Pro 4 možnosti musíme aplikovat WREG & b’11’.
Pro jistotu uvedu, jak logická operace AND (nebo také logický součin) funguje. AND přijímá dva vstupní bity a vrací jeden.
Zde je pravdivostní tabulka:
0, 0 -> 0
0, 1 -> 0
1, 0 -> 0
1, 1 -> 1
Pokud se podíváte pozorně, zjistíte, že se opravdu jedná o součin. Pro vícebitová čísla platí stejná pravidla, operace se provede bit po bitu. V našem případě tedy “b’xxxxxxxx’ & b’11′” vrátí hodnotu b’000000xx’. Ta je dvoubitová, takže může nabývat hodnot 0 – 3.
V assembleru pro operaci AND máme instrukce andwf (provede AND mezi registry W a f) a andlw (provede AND mezi registem W a konstantou).
Náš assemblerovský podprogram pro segmenty bude vypadat takto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
disp_hex_cislo: andlw 0x0F ;ochrana proti nechtěným hodnotám brw ;(vyšším než 15) retlw b'00100001' ; 0 retlw b'11100111' ; 1 retlw b'00110010' ; 2 retlw b'10100010' ; 3 retlw b'11100100' ; 4 retlw b'10101000' ; 5 retlw b'00101000' ; 6 retlw b'11100011' ; 7 retlw b'00100000' ; 8 retlw b'10100000' ; 9 retlw b'01100000' ; A retlw b'00101100' ; B retlw b'00111001' ; C retlw b'00100110' ; D retlw b'00111000' ; E retlw b'01111000' ; F |
Instrukcí retlw se MCU vrátí z podprogramu k provádění předchozího kódu a tím se zabrání v provádění dalších nežádoucích instrukcí. Pokud bychom měli např. jen 13 možností a masku 0x0F, pak bychom na zbylá 3 místa vložili instrukci nop. S tímto případem se možná v následujících dílech setkáme.
Dále budeme potřebovat podprogram, který převezme výsledek předchozího podprogramu a podle něj nastaví registry LATA a LATC. Nejjednodušší způsob je vzít první tři bity z výsledku a přesunout je do LATA, poté vzít posledních 5 bitů z výsledku a přesunout je do LATC. K tomu budeme potřebovat logický bitový posun.
Logický bitový posun může být doprava nebo doleva. Než abych se pouštěl do slovního vysvětlování, vypůjčím si obrázek z anglické wikipedie (https://en.wikipedia.org/wiki/Bitwise_operation):
A stejně pro jistotu popíšu…
Všechny bity se posunou doleva/doprava a krajní bit vlevo/vpravo zanikne, zatímco protější krajní bit vpravo/vlevo je doplněn nulou.
Příklad.:
Na konstantu b’10010100′ použijeme operaci levý logický posun. Výsledek bude b’00101000′. Tato operace je ekvivalntní operaci násobení dvěma a vychází z matematiky dvojkové soustavy. Pokud bychom udělali “levý číslicový posun” v desítkové soustavě (přidání nuly napravo), byla by tato operace ekvivalentní operaci násobení deseti. Pro pravý bitový posun platí obdobně, že je ekvivalentní operaci dělení dvěma.
Assembler nám poskytuje operace lslf (Logical Shift Left f) a lsrf (Logical Shift Right f).
Náš podprogram udělá následující kroky:
Přijme hodnotu z registru W. Tuto hodnotu si uloží do pomocného registru. Aplikuje operaci WREG & b’111′ a výsledek přesune do registru LATA. Poté načte původní hodnotu z pomocného registru do W a aplikuje tři pravé bitové posuny. Výsledek je přesunut do registru LATC.
A zde je zdrojový kód:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
disp_zobraz: banksel R_POM movwf R_POM andlw b'111' banksel LATA movwf LATA banksel R_POM movfw R_POM lsrf WREG lsrf WREG lsrf WREG banksel LATC movwf LATC return |
Nyní, když máme vše potřebné k zobrazování číslic na displeji, můžeme přistoupit k hlavnímu programu. V něm vytvoříme čítač. Hodnotu tohoto čítače proženeme oběma rutinami a následně čítač inkrementujeme. Náš displej by měl počítat od 0 do F (v šestnáctkové soustavě) stále dokola.
A tady je celý zdrojový kód:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
#include "p16f1705.inc" ; CONFIG1 ; __config 0xC9E4 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF ; CONFIG2 ; __config 0xDEFB __CONFIG _CONFIG2, _WRT_OFF & _PPS1WAY_OFF & _ZCDDIS_ON & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LPBOR_OFF & _LVP_OFF #define CITAC .100 R_CITAC equ 0x20 ;dekrementující čítač R_POM equ 0x21 ;univerzální pomocná proměnná R_DISPCITAC equ 0x22 ;hodnota zobrazovaná na displeji org 0 goto START ;====== PODPROGRAMY ============================================================ ;vezme cislo 0 - 15 z registru W a vrátí ;odpovídající segmenty na displeji ;ve formátu: <C4:C3:C2:C1:C0:A2:A1:A0> disp_hex_cislo: andlw 0x0F ;ochrana proti nechtěným hodnotám brw ;(vyšším než 15) retlw b'00100001' ; 0 retlw b'11100111' ; 1 retlw b'00110010' ; 2 retlw b'10100010' ; 3 retlw b'11100100' ; 4 retlw b'10101000' ; 5 retlw b'00101000' ; 6 retlw b'11100011' ; 7 retlw b'00100000' ; 8 retlw b'10100000' ; 9 retlw b'01100000' ; A retlw b'00101100' ; B retlw b'00111001' ; C retlw b'00100110' ; D retlw b'00111000' ; E retlw b'01111000' ; F ;------------------------------------------------------------------------------- ;načte segmenty ve formátu <C4:C3:C2:C1:C0:A2:A1:A0> ;z registru W a přiřadí je do registrů LATA a LATC disp_zobraz: banksel R_POM movwf R_POM andlw b'111' banksel LATA movwf LATA banksel R_POM movfw R_POM lsrf WREG lsrf WREG lsrf WREG banksel LATC movwf LATC return ;====== INICIALIZACE PROGRAMU ================================================== START: ;nastavení vnitřního oscilátoru na frekvenci 4MHz movlw b'01101010' banksel OSCCON ;je třeba vybrat správnou banku movwf OSCCON ;nastavení dekrementujícího čítače movlw CITAC ;čítač začíná na hodnotě .100 banksel R_CITAC movwf R_CITAC ;nastavení timeru 4 banksel T4CON movlw b'1001101' ;1:10 postscaler, tmr on, 1:4 prescaler movwf T4CON movlw .250 movwf PR4 ;nastavení IO pro displej banksel TRISA ;odpovídající segmenty na displeji jsou movlw b'111000' ;výstupy, zbytek jsou vstupy movwf TRISA movlw b'100000' movwf TRISC banksel ANSELA ;všechny piny jsou digitální clrf ANSELA clrf ANSELC banksel LATA ;segmenty nesvítí movlw b'111' movwf LATA movlw b'11111' movwf LATC banksel R_DISPCITAC clrf R_DISPCITAC ;====== HLAVNÍ PROGRAM ========================================================= HLAVNI_SMYCKA: banksel PIR2 ;testujeme timer4 btfss PIR2,TMR4IF goto HLAVNI_SMYCKA ;čítač je platný ;1Hz bcf PIR2,TMR4IF ;vynulování TMR4IF banksel R_CITAC decfsz R_CITAC ;je čítač na nule? goto HLAVNI_SMYCKA ;není, vracíme se zpět na začátek movlw CITAC ;je movwf R_CITAC ;čítač nastavíme na výchozí hodnotu movfw R_DISPCITAC call disp_hex_cislo call disp_zobraz banksel R_DISPCITAC incf R_DISPCITAC goto HLAVNI_SMYCKA end |
A to je pro dnešek vše.