[PIC programování] – 11. díl – bonus
Vítám vás u malého bonusu k 11. dílu!
Napadlo mě malé kosmetické vylepšení našeho programu. Pokud je zobrazované číslo na displeji menší než 16, pak je v šestnáctkové soustavě jednociferné. Doposud jsme na displeji i jednociferné číslo zobrazovali jako dvojciferné, akorát s nulou na začátku. Toto chování můžeme snadno upravit využitím Carry bitu v registru STATUS (zmíněném v minulém bonusu).
Pro jasnost kódu vysvětlím chování Carry bitu. Pokud budem přičítat a dojde k přetečení registru, pak Carry bit bude roven 1, pokud k tomu nedojde, bude naopak Carry roven 0. V případě odčítání má Carry bit opačnou logiku. Při podtečení registru, nebo pokud je výsledek nulový, bude Carry = 0, jinak 1.
Odčítání má toto chování proto, poněvadž se provádí jako sčítání první hodnoty s dvojkovým doplňkem druhé hodnoty.
Co je proboha dvojkový doplněk?
Dvojkový doplněk (v angličtině Two’s complement) je způsob kódování záporného čísla v binární podobě). Pokud máme 8-bitové číslo, můžeme jej buď reprezentovat hodnotami 0 až 255 nebo -128 až 127. Např. v jazyce C se rozlišuje typ signed (znaménkový) a unsigned (bezznaménkový).
Ukážu pár čísel v různých reprezentacích:
8-bit binární forma | 8-bit signed | 8-bit unsigned |
b’11111110′ | -2 | 254 |
b’11111111′ | -1 | 255 |
b’00000000′ | 0 | 0 |
b’00000001′ | 1 | 1 |
b’00000010′ | 2 | 2 |
Znaménková reprezentace dává smysl, protože pokud bychom od nuly odečetli 1, dojde k podtečení na hodnotu 0xFF (b’11111111′). Nyní se nabízí otázka, jak ke kladnému číslu vypočítám záporné? Jednoduše. Vezmu bitovou negaci čísla a přičtu k ní jedničku.
Pojďme si ukázat příklad na čísle 1 = b’00000001′ = 0x01
Nejprve negace: ~1 = ~b’00000001′ = b’11111110′ = 254
Potom inkrementace: 254 + 1 = 255 = b’11111111′
Hm… Ono to funguje! Sedí nám to s tabulkou nahoře! Operaci, kterou jsme právě udělali se říká dvojkový doplněk. A to je celá magie.
Tak si pojďme ukázat chování Carry bitu s reprezentací sublw jako addlw s dvojkovým doplňkem. Vezmeme případ, kdy se obě čísla rovnají a my je odčítáme:
máme WREG = b’00000100′ = 4
pokud provedeme sublw 4 (= sublw b’00000100′), je to stejné jako dvě operace addlw b’11111011′, incf WREG, tedy:
1 2 3 4 5 6 7 8 9 10 11 |
movlw 4 ;WREG = 4 = b'00000100' sublw 4 ;WREG = 0, Carry = 1, protože je to to samé jako: movlw 4 ;WREG = 4 addlw b'11111011' ; = ~4 ;WREG = 255 incf WREG ;WREG = 0, Carry = 1, protože došlo k přetečení |
Abych to nějak zjednodušil: Carry = 1 jen v případě, že dojde k přetečení registru. Jestliže k tomu nedojde (ani po nahrazení sublw instrukcí addlw), Carry = 0. Doufám, že se jsem to vysvětlil dostatečně srozumitelně. Pokud ne, pište prosím do komentářů.
Takže stačí, když do hlavního programu přidáme pod návěští __mpx_d1 (část kódu obsluhující první cifru/displej) tento kód:
1 2 3 4 |
movfw R_DISP sublw .15 ;je zobrazovaná hodnota na displeji btfsc STATUS,C ; jednociferná? goto __mpx_konec ;ano, zhasnu levou cifru (= 0) |
Myslím, že komentáře mluví za vše. Kód je jednoduchý, ale je třeba se nad ním pořádně zamyslet, než jej člověk pochopí.
A zde je pro jistotu 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 162 163 164 165 |
#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 sublw .15 ;je zobrazovaná hodnota na displeji btfsc STATUS,C ; jednociferná? goto __mpx_konec ;ano, zhasnu levou cifru (= 0) 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 |
A to je pro tento bonus vše!