[PIC programování] – 11. díl
Vítám vás u dalšího dílu ze série PIC programování!
Dnes se naučíme číst analogové vstupy a trochu upravíme zobrazovací rutinu pro 7-segmentový displej.
Analogový vstup
Analogový vstup je v našem případě vstup, na kterém může, vůči zemi, objevit napětí 0V až 5V. Tuto hodnotu pak můžeme číst jako n-bitové číslo. Abychom toto číslo získali, použijeme tzv. AD převodník (Analog to Digital, ADC – Analog to Digital Converter). Naše PICka je vybavena 10-bitovým AD převodníkem. To znamená, že hodnotu na našem analogovém vstupu umíme “rozkouskovat” do hodnot 0 – 1023. Náš převodník má tedy rozlišení zhruba 4,882 mV (5 / 1024 V). Hodnota 0 pro napětí 0V a hodnota 1023 pro napětí 5V.
Nastavení převodníku je velmi jednoduché a zahrnuje výběr referencí pro nulové a kladné napětí, výběr časování, výběr analogového vstupu, zapnutí/vypnutí AD převodníku a pár dalších věcí, které ale dnes nevyužijeme.
Než se pustíme do nastavování AD převodníku, budeme muset trochu upravit zapojení našich 7-segmentových displejů, protože v aktuálním zapojení nemáme k dispozici žádný analogový vstup.
Zde je nové schéma, jako analogový vstup použijeme 10kΩ trimr.
Pozn.: Displej 2 byl přesunut na pin C5. Pin A4 nyní používáme jako analogový vstup.
U dílu 10 měl kolega petrnik v komentářích několik dobrých připomínek, podle kterých jsem zavedl pár změn v kódu. Pojďme se na ně podívat.
Náš mikrokontrolér PIC16F1705 má k dispozici na adresách 0x70 až 0x7F tzv. Common RAM. Stejně jako banky je toto specialitou architektury PIC. Common RAM se od zbytku paměti RAM (až na některé speciální registry) liší tím, že je dostupná z jakékoliv banky, tím odpadá potřeba přepínat na správnou banku. Tyto registry se velmi hodí pro univerzální proměnné do podprogramů, kde mohou být využity pro pomocné výpočty, tím se dá poměrně krásně zefektivnit náš program. Náš pomocný registr R_POM tedy přesuneme do Common RAM na adresu 0x70.
Druhá změna je u několika příkazů typu (addwf, andwf, lsrf, …), tedy příkazů, kde se nějak operuje s daty v libovolném registru f. U těchto příkazů jsme doposud specifikovali pouze první operand (registr f). Druhý operand za nás automaticky doplnil kompilátor. Nyní začneme psát i druhý operand, který říká, zda se má výsledek uložit do registru W nebo f. Důvod je jednoduchý. Nyní náš kompilátor automaticky doplňuje volbu ukládání do registru f. Mohlo by se ale v budoucnu stát, že se toto výchozí nastavení změní. Tím by náš program při nové kompilaci nefungoval správně. Druhý operand může u těchto instrukcí nabývat hodnot 0 nebo 1. Naštěstí v zahrnutém includu (“p16f1705.inc”) máme nadefinované zástupné symboly W a F, takže si nemusíme pamatovat, zda 0 / 1 je pro W nebo F a opačně. Pokud např. napíšeme “andwf R_POM,F“, máme jistotu, že se výsledek vždy uloží do registru R_POM, nezávisle na nastavení kompilátoru.
R_POM přejmenujeme na RC_POM. Písmeno C v prefixu názvu nám bude napovídat, že jde o registr v Common RAM.
Po uvedených změnách, vypadá náš upravený podprogram pro nastavení registrů LATx takto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
;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: movwf RC_POM ;uložím si nastavení LATx movlw b'111000' banksel LATA andwf LATA,F ;vynuluji bity segmentů movfw RC_POM andlw b'111' iorwf LATA,F ;nastavím pouze bity segmentů movlw b'100000' andwf LATC,F ;vynuluji bity segmentů movfw RC_POM lsrf WREG,F lsrf WREG,F lsrf WREG,F iorwf LATC,F ;nastavím pouze bity segmentů return |
Upravená definice RC_POM vypadá samozřejmě takto:
1 |
RC_POM equ 0x70 ;univerzální pomocná proměnná |
Pozn.: V případě instrukce “lsrf WREG,F” je samozřejmě použití druhého operandu zbytečné, protože se výsledek tak jako tak uloží do registru W. Nicméně pro udržení nějaké konzistence kódu jej zde ponechávám.
Pojďme se nyní zaměřit na nastavení AD převodníku. K jeho úplnému nastavení nám postačí pouze dva registry – ADCON0 a ADCON1. Začneme registrem ADCON0, zde je jeho struktura:
Bity CHS<4:0> (ADCON0<6:2>) připojíme AD převodník k určitému analogovému vstupu. Náš analogový vstup na pinu A4 je podle datasheetu (na straně 5) označen také jako AN3. Zde je tabulka výběru připojeného vstupu:
Z této tabulky je patrné, že hodnota CHS pro nás bude b’00011′. Dále je zde bit GO/DONE, kterým později aktivujeme převod analogové hodnoty na digitální, zatím jej ponecháme v nule. Posledním bitem, ADON, aktivujeme/deaktivujeme celý AD převodník. Ten můžeme nechat po dobu celého programu zapnutý, počáteční hodnota tedy bude 1.
Pokračujeme registrem ADCON1:
Výsledek převodu analogové na digitální hodnotu je 10-bitový. Ukládá se do dvou registrů – ADRESH a ADRESL. Bitem ADFM určujeme, zda bude výsledek zarovnán doprava nebo doleva. Zde je výstižný obrázek z datasheetu:
Protože spodní dva bity výsledku zanedbáme (tedy použijeme pouze horních 8 bitů), zvolíme zarovnání doleva (ADFM = 0). Výsledek pak budeme brát jen z registru ADRESH. Pomocí bitů volíme časování AD převodníku. Časování nevolíme náhodně, ale podle frekvence oscilátoru a vhodného časování podle následující tabulky z datasheetu:
My jsme frekvenci oscilátoru (Fosc) nastavili na 4 MHz. Z následující tabulky se pro nás jako vhodná hodnota časování AD převodníku jeví Fosc/8 (šedá pole značí nevhodná časování). Alternativně bychom mohli použít časování Fosc/4 a Fosc/16. Převod podle tabulky pak bude pro Fosc/8 na frekvenci 4 MHz trvat 2 μs. ADCS tedy zvolíme b’001′. ADNREF a ADPREF bity určují reference pro kladné a nulové napětí. Tyto hodnoty ponecháme výchozí (0), tím jsou VDD a VSS, tedy 5V a GND.
Celé nastavení AD převodníku v kódu vypadá následovně:
1 2 3 4 5 |
banksel ADCON1 ;výsledek zarovnáme doleva, referenční movlw b'00010000' ; napětí jsou VDD (5V), VSS (GND), movwf ADCON1 ; časování Fosc/8 movlw b'00001101' ;AD převodník připojen na RA4 vstup, movwf ADCON0 ; zapnutý |
Zbývá nám tedy už jen vyřešit samotný převod analogové hodnoty do digitální podoby. Kód je velmi jednoduchý. Poté, co připojíme analogový vstup k AD převodníku, nastavíme bit ADCON0<1> (bit GO/DONE) na hodnotu 1. Po dokončení převodu AD převodník tento bit shodí do 0. Stačí nám tedy tento bit dokola testovat a jakmile skočí do 0, víme, že převod skončil, a můžeme výslednou hodnotu z registru ADRESH zobrazit na displeji. Kód vypadá následovně:
1 2 3 4 5 6 7 |
banksel ADCON0 bsf ADCON0,1 ;zapneme AD převod na pinu RA4 btfsc ADCON0,1 ;čekáme, dokud není převod dokončen goto $-1 movfw ADRESH ;přesuneme výsledek do registru, jehož banksel R_DISP ; hodnotu zobrazujeme na displejích movwf R_DISP |
Tento kód vykonáváme každou půl sekundu a to z jednoho prostého důvodu. Kdybychom hodnotu na 7-segmentovém displeji (a to platí obecně) měnili příliš často, hodnota by kvůli (v tomto případě nežádoucí) persistenci vidění přestala být čitelná.
A takto vypadá celý program:
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
#include "p16f1705.inc" __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF __CONFIG _CONFIG2, _WRT_OFF & _PPS1WAY_OFF & _ZCDDIS_ON & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LPBOR_OFF & _LVP_OFF #define HCITAC .50 #define TECKA b'11011111' R_HCITAC equ 0x20 ;dekrementující čítač - hl. smyčka R_DISP equ 0x22 ;hodnota zobrazovaná na displeji R_MPXCITAC equ 0x23 ;čítač, který rozhoduje o aktivním ; displeji RC_POM equ 0x70 ;univerzální pomocná proměnná 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: movwf RC_POM ;uložím si nastavení LATx movlw b'111000' banksel LATA andwf LATA,F ;vynuluji bity segmentů movfw RC_POM andlw b'111' iorwf LATA,F ;nastavím pouze bity segmentů movlw b'100000' andwf LATC,F ;vynuluji bity segmentů movfw RC_POM lsrf WREG,F lsrf WREG,F lsrf WREG,F iorwf LATC,F ;nastavím pouze bity segmentů return ;====== INICIALIZACE PROGRAMU ================================================== START: ;nastavení vnitřního oscilátoru na frekvenci 4MHz movlw b'01101010' banksel OSCCON movwf OSCCON ;nastavení dekrementujícího čítače movlw HCITAC banksel R_HCITAC movwf R_HCITAC ;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 displeje + analogový vstup banksel TRISA ;odpovídající segmenty na displeji a movlw b'011000' ;PNP tranz. jsou výstupy, zbytek vstupy movwf TRISA clrf TRISC banksel ANSELA ;všechny piny jsou digitální clrf ANSELA bsf ANSELA,4 ;kromě RA4, to je analogový vstup clrf ANSELC banksel LATA movlw b'100111' ;displeje jsou zhasnuté movwf LATA movlw b'111111' movwf LATC ;nastavení AD převodníku banksel ADCON1 ;výsledek zarovnáme doleva, referenční movlw b'00010000' ; napětí jsou VDD (5V), VSS (GND), movwf ADCON1 ; časování Fosc/8 movlw b'00001101' ;AD převodník připojen na RA4 vstup, movwf ADCON0 ; zapnutý ;vynulování uživatelských registrů banksel R_DISP clrf R_DISP clrf R_MPXCITAC ;====== HLAVNÍ PROGRAM ========================================================= HLAVNI_SMYCKA: banksel PIR2 ;testujeme timer4 btfss PIR2,TMR4IF goto HLAVNI_SMYCKA bcf PIR2,TMR4IF ; --- zde provedeme multiplexovou rutinu ------------------------------------- banksel R_MPXCITAC incf R_MPXCITAC,F btfss R_MPXCITAC,0 ;Je mpx čítač lichý? goto __mpx_d1 ;Každou sudou periodu rozsvítíme disp 1 goto __mpx_d2 ;Každou lichou periodu rozsvítíme disp 2 __mpx_d1: movfw R_DISP swapf WREG,F call disp_hex_cislo banksel LATC bsf LATC,5 ;zhasneme disp 2 call disp_zobraz banksel LATA bcf LATA,5 ;rozsvítíme disp 1 goto __mpx_konec ;přeskočíme nežádoucí kód __mpx_d2: movfw R_DISP call disp_hex_cislo banksel LATA bsf LATA,5 ;zhasneme disp 1 call disp_zobraz banksel LATC bcf LATC,5 ;rozsvítíme disp 2 __mpx_konec: ; ---------------------------------------------------------------------------- banksel R_HCITAC decfsz R_HCITAC,F ;je čítač na nule? goto HLAVNI_SMYCKA ;není, vracíme se zpět na začátek movlw HCITAC ;je, movwf R_HCITAC ; čítač nastavíme na výchozí hodnotu banksel ADCON0 bsf ADCON0,1 ;zapneme AD převod na pinu RA4 btfsc ADCON0,1 ;čekáme, dokud není převod dokončen goto $-1 movfw ADRESH ;přesuneme výsledek do registru, jehož banksel R_DISP ; hodnotu zobrazujeme na displejích movwf R_DISP goto HLAVNI_SMYCKA end |
Na závěr bych jen chtěl dodat, že veškeré podrobné informace k AD převodníku najdete v datasheetu na straně 218 a pro jistotu ještě jednou uvedu odkaz:
http://ww1.microchip.com/downloads/en/DeviceDoc/40001729C.pdf.
A to je pro tento díl vše!