2014 m. lapkričio 26 d., trečiadienis

Dvejetainiai skaičiai

Dvejetainiai skaičiai taip stipriai susįję su skaitmenine technika, kad jų pagrindų nežinojimas apsunkina bet kokius žingsnius link mikrovaldiklių ar apskritai imant programavimo. Mane, pamenu, mokykloje mokė šių informatikos pagrindų, o kadangi informatikos klasėje nebuvo kompiuterių, tai nebuvo ir pagundos žaist žaidimų ar kitokių nukrypimų daryt. Dabar, kiek suprantu, po žodžiu "informatikos pamoka" slepiasi paprasčiausias kompiuterinis raštingumas, nors galiu ir klysti. Tačiau čia rašysiu ne apie švietimo programas.

Šiandien vakare bandžiau pasakoti, su kuo valgomi dvejetainiai skaičiai mikrovaldiklių kontekste, tačiau žodžiu man tokius dalykus sunku paaiškint, nes pradedu blaškytis ir finale gaunas baisiai painiai. Todėl pabandysiu čia aprašyti kuo paprasčiau ir iš eilės, mažu kam padės. Tačiau jeigu esate susipažinęs su šia tema - šio straipsnio skaityti gal ir neverta, nes gali būti juokinga.

Dvejetainė skaičiavimo sistema
Realiame gyvenime mes naudojame dešimtainę skaičiavimo sistemą - jau pagal pavadinimą galima numanyti, kad tokia sistema naudoja dešimt skirtingų simbolių, kuriais galima užrašyti bet kokį skaičių. Tačiau kompiuteriams toks skaičiavimo ir skaičių saugojimo būdas nėra priimtinas dėl techninių priežasčių. Kompiuterio atmintis yra sudaryta iš daugybės tranzistorių, kuris veikia kaip jungtukas, t.y. gali būti arba įjungtas, arba išjungtas - toks jungtuko ekvivalentas yra pats mažiausias informacijos kiekis - bitas, kuris gali būti lygus, kaip jau turbūt aišku, tik arba 1 (jungtukas įjungtas) arba 0 (jungtukas išjungtas). Norint išsaugoti vieną dešimtainį skaitmenį reiktų dešimties tokių tranzistorių, t.y. bitų. Vienam skaitmeniui naudoti dešimtį bitų būtu nemenka prabanga, todėl ir naudojama dvejetainė skaičiavimo sistema. Jos atveju, kaip ir sako pavadinimas, naudojami tik du skaitmenys: 1 ir 0, kad itin patogu kompiuteriams. Ir tais dviem skaičiais, žinoma, galima užrašyti bet kokį skaičių, o naudojamų bitų skaičius konkrečiam skaičiui priklauso nuo skaičiaus dydžio, tačiau, kaip pamatysime vėliau, jis mažesnis, nei būtų reikalingas saugoti dešimtainius skaičius.
Dvejetainiai skaičiai mums yra neįprasti, todėl didesnius nei keleto dešimčių skaičius sudėtinga įsiminti ar mintinai versti iš vienos sistemos į kitą. Tai, žinoma, galima padaryti ir su skaičiuotuvo pagalba (Windows sistemoje esantis skaičiuotuvas irgi puikiai su tuo susidoroja), tačiau praverčia ir mokėjimas tai daryti jeigu ne mintinai, tai bent popieriaus ir tušinuko pagalba.
Turėdami dešimtainį skaičių ir norėdami jį paversi į dvejetainę formą, turime jį dalinti iš dviejų: jei skaičius dalinasi iš dviejų,, bitas lygus nuliui, jei lieka liekana - bitas lygus vienam. Tada padalintą skaičių dar kartą daliname iš dviejų, vėl užrašydami liekaną, ir taip iki kol nebelieka ką dalinti.
Kadangi eilinį kartą paaiškinau painiai, bandau demonstruoti. Pradėkime nuo mažy skaičių, pavyzdžiui, paverskime į dvejetainę formą skaičių 13. Padalinę iš dviejų, gauname 6,5, taigi rašome žemiau sveikąją dalį, o už brūkšnio - liekaną. Skaičius 6 tvarkingai dalinasi iš 2, todėl dešinėje pusėje liekanos nėra, rašome nulį, o žemiau rašome dalybos rezultatą, t.y. 3. Šis iš dviejų vėl gražiai nesidalina, todėl dalybos rezultatas yra 1 ir liekana, rašoma dešinėje, taip pat 1. Ir vienetas iš dviejų taip pat nesidalina, todėl dešinėje rašome paskutinį vienetą ir skaičiavimas baigtas.
13 | 1
6  | 0
3  | 1
1  | 1

Dvejetainį skaičių pradedame rašyti nuo apačios, taigi, gauname kad 1310 lygus 11012. Indeksas po skaičiumi rodo skaičiavimo sistemą, tačiau programuojant naudojamas kitas užrašymo būdas - dešimtainiai skaičiai rašomi be jokių ženklų, o dvejetainiai - 0b1101, tačiau kol kas tai esmės nekeičia.
Tokiu būdu galima iš dešimtainės į dvejetainę pakeisti bet kokio dydžio skaičius, pavyzdžiui:
27468 | 0
13734 | 0
6867  | 1
3433  | 1
1716  | 0
858   | 0
429   | 1
214   | 0
107   | 1
53    | 1
26    | 0
13    | 1
6     | 0
3     | 1
1     | 1
0
Taigi, 2746810 lygus 1101011010011002.
Dvejetainio skaičiaus vertimas į dešimtainį lygiai toks pat nesudėtingas skaičiavimo prasme, tik prieš tai naudojome dalybą, o čia bus naudojama sudėtis. Turiu iškart pripažinti, kad nesigilinsiu į matematines formules ir apibrėžimus - rašysiu taip, kaip man pačiam patogu rikaip aš tai darau.
Dvejetainio skaičiaus vertimui į dešimtainį svarbu kiekvieno vienetuko ar nuliuko pozicija. Kadangi kompiuteriai praktiškai visur pradeda skaičiuoti nuo nulio, pirmą poziciją ir mes pasižymėsime nuliu. Tačiau reikia pastebėti, kad pirma pozicija yra pati dešinioji, kas taip pat kiek neįprasta. Imkime nedidelį skaičių, ir sudarome lentelę:
Dvejetainis skaičius1111011
Pozicija6543210
Vertė6432168421

Antroje eilutėje surašiau bito poziciją: pirmu bitu laikome patį dešinį bitą, ir žymime jį 0, paskutiniu laikome patį kairįjį bitą, ir žymime jį 6. Viso turime 7 bitus. Po kiekviena pozicija susirašome to bito vertę. Šie skaičiai yra gaunami skaičių 2 pakėlus laipsniu, lygiu pozicijos skaičiui, t.y. 20 yra 1, o 26 yra 64. Ir tada tiesiog sudedame tas vertes, virš kurių yra vienetukai, nulius palikdami ramybėje. Šiuo atveju sudedame visas vertes, išskyrus antros pozicijos vertę - ketvertą: 1+2+8+16+32+64=123, ką ir turėjome gauti - pasitikrinkite su skaičiuotuvu.

Kadangi bitas yra pats mažiausias informacijos kiekis, informacijos kiekiui nusakyti dažniau naudojamas baitas, kuris yra lygus 8 bitams. 1 baitas gali įgyti 256 skirtingas reikšmes: dešimtainėje sistemoje nuo 0 iki 255, o dvejetainėje sistemoje - nuo 00000000 iki 11111111. Esant poreikiui įvardinti kitokius bitų skaičius, naudojamas "nibble" terminas, kuriam vertimo nežinau - jis lygus pusei baito, t.y. keturiems bitams. Didesniems nei baitas nusakyti naudojamas žodžio ("WORD") terminas, reiškiantis 16 bitų skaičių, kuris apima nuo 0 iki 65535 dešimtainėje sistemoje. Yra ir didesniems skaičiams, bet jų mikrovaldikliuose nepanaudosite, kaip DWORD ar QWORD.

Labai svarbi Būlio algebra
Operuojant dvejetainiais skaičiais, apie kurių praktinį pritaikymą papasakosiu vėliau, labai svarbu žinoti, kaip norimą bitą pakeisti pagal esamą situaciją. Tam naudojamos loginės operacijos, arba Būlio algebra.
Kaip jau minėjau, dvejetainiuose skaičiuose yra tik dvi skaičiaus būsenos: vienetas arba nulis, kas atitinka yra/nėra, tiesa/netiesa. Informatikoje dažniausiai naudojamos sąvokos TRUE/FALSE, tačiau čia dėl tingėjimo rašyti naudosiu 1 ir 0 atitinkamai.
Programuojant mikrovaldiklį gali prireikti kažkurį baito bitą pakeisti iš nulio į vienetą arba atvirkščiai, arba tiesiog pakeisti bito būseną, nežinant, kokia ji yra šiuo metu. Tam pravers pagrindinės loginės operacijos IR, ARBA ir NE. Čia aprašinėsiu tik dviejų bitų logines operacijas.

Loginis IR (AND, &, loginė daugyba) - sąlyga teisinga tik tada, kai abu bitai yra 1. Teisingumo lentelė atrodo taip:
1 bitas2 bitas1bitas & 2bitas
000
010
100
111

Kad būtų lengviau įsivaizduoti, tarkime, kad yra koridorius su dvejomis durimis. Kai durys uždarytos - jos simbolizuos loginį nulį, t.y. pro jas nebus galima praeiti; kai atidarytos - loginį vienetą. Tokiu atveju koridorius įgyja loginę reikšmę, lygią vienetui, tik tada, kai galima pro jį praeiti, o praeiti galima tik kai abi durys atidarytos, t.y. lygios loginiam vienetui. Jei bent vienos durys uždarytos, pro koridorių praeiti nebus įmanoma.

Loginis ARBA (OR, |, loginė sudėtis) - salyga teisinga tik tada, kai bent vienas iš bitų yra lygus vienetui. Teisingumo lentelė:
1 bitas2 bitas1bitas | 2bitas
000
011
101
111

Grįžtant prie durų pavyzdžio, šįkart bus ne koridorius, o siena su pora durų, kurios yra viena šalia kitos, ant tos pačios sienos. Tokiu atveju praeiti bus galima tik tada, jeigu bent vienos durys bus atidarytos.

Loginis NE (NOT, ~, loginis neigimas) - veiksmas, tiesiog pakeičiantis bitą iš vieneto į nulį arba atvirkščiai. Teisingumo lentelė:
1 bitas~ bitas (NOT)
01
10

Neigimo operacija paprastai naudojama su vienu bitu, tuo tarpu kitos operacijos - su dviem ir daugiau. Tačiau tai nėra taisyklė.
Yra ir daugiau loginių operacijų, tačiau kol kas jų neaptarinėsiu.

Kaip visa tai susįję su mikrovaldikliais?
Mano čia aprašinėjami mikrovaldikliai yra 8 bitų, t.y. jie daugumoje atveju naudoja 8 bitų adresaciją ir, kas svarbiausia, visi fiziniai prievadai yra būtent 8 bitų. Jei pažiūrėti į bet kurio ATMEL 8 bitų mikrovaldiklio aprašymą, fiziniai prievadai, jei kalbėt tik apie pačius paprasčiausius, įvesties ir išvesties, turi kiekvienas po 8 duomenų linijas, t.y. aštuoni fiziniai mygtukai, kurie gali būti įjugiami arba išjungiami nepriklausomai vienas nuo kito, t.y. kiekvienas iš jų gali būti vienetas arba nulis. Pavyzdžiui, pirmi bandymai su mikrovaldikliais prasideda nuo diodų mirksinimo: aštuoni šviesos diodai prijungiami prie mikrovaldiklio prievado "kojų": jei šviesos diodas dega, reiškia, kad ant konkrečios "kojos" yra įtampa, o tai savo ruožtu reiškia, kad tame 8 bitų prievado bite yra loginis vienetas. Iliustruoti tokį prievadą galima šitaip:


Nupiešiau tipinį ATMEL mikrovaldiklio prievadą - AVR mikrovaldikliuose prievadai (PORT) žymimi raidėmis, taigi gali būti PORT A, PORT B, PORT C ir t.t. - prievadų gali (ir yra) būti ne vienas, o konkretus jų kiekis priklauso nuo mikrovaldiklio modelio. Kaip jau minėjau, kiekvienas prievadas turi 8 fizinius kontaktus (jie gali būti programiškai nustatyti tiek kaip išėjimai, pavyzdžiui, uždegti arb aužgesinti šviesos diodą, arba kaip įėjimai, pavyzdžiui, mygtuko prijungimui, tačiau apie tai šį kartą nepasakosiu, ir laikysiu, kad visi jie yra sukonfigūruoti kaip išėjimai). Kiekvienas iš jų turi pavadinimą, pavyzdžiui, PA0 (Port A 0bit), kuriuos surašiau paveiksliuke. Skaičius prie pavadinimo atitinka bito poziciją, kaip taip pat matyti iš paveikslėlio. Kad būtų aiškiau, surašiau ir kiekvieno bito vertę, kuri reikalinga, kaip jau žinome, paskaičiuoti dešimtainį skaičių. Prievadas valdomas tiesiog priskiriant prievadui skaičių, kuris gali būti tiek dešimtainis, tiek dvejetainis. Pavyzdžiui, paveikslėlio atveju visi prievado bitai turi loginę reikšmę "0", todėl ir dešimtainis skaičius, priskirtas prievadui yra lygus nuliui. Paprasčiausia komanda įgyvendinti tokį prievado bitų išjungimą C programavimo kalboje yra tokia:

PORTA = 0;
 Šia komanda prievadui priskiriamas dešimtainis skaičius "0". Kartais patogu tą patį priskyrimą rašyti dvejetainiu kodu:

PORTA = 0b00000000;


Rezultatas bus tas pats, tačiau aiškiai matosi, kurie bitai yra nuliai, kurie vienetai (šiuo atveju, kaip matyti, visi yra nuliai.

Pabandykime "uždegti" keletą šviesos diodų. Vaizdelis gausis panašus į šį:

Tam, kad gauti tokį rezultatą, prievadui reikia priskirti dešimtainį skaičių "10" - kodėl, manau, aiškinti nereikia, o jei neaišku - skaityti aukščiau. Priskyrimas naudojant dešimtainį skaičių atrodys šitaip:

PORTA = 10;

Tačiau kur kas aiškiau bus, jei priskyrimą rašysime taip:

PORTA = 0b00001010;


Jau iš priskyrimo sakinio aišku, kurie bitai įjungti, kurie išjungti.

Bandom uždegti daugiau švieselių:

Šiuo atveju prievadui PORT A priskiriamas dešimtainis skaičius 170 arba dvejetainis skaičius 0b10101010.
Ateičiai dar pastebėsiu, kad visas prievadas nebūtinai turi būti vien įėjimai: dalis prievado bitų gali būti sukonfigūruoti kaip įėjimai, dalis - kaip įšėjimai. Tačiau tai ne šio straipsnio tema.

Veiksmai su prievado bitais, arba prasideda loginės linksmybės

Kai reikia priskirti visus prievado bitus iš karto, toks priskyrimas, kurį aprašiau aukščiau, yra tinkamas. Tačiau ką daryti, jei man reikia uždegti tik vieną šviesos diodą, nepakeičiant kitų būsenos? Tarkim, jei situacija tokia, kaip paskutiniame paveikslėlyje, ir man papildomai reikia uždegti šviesos diodą, prijungtą prie PA0, tačiau aš nežinau, kurie diodai šiuo metu šviečia, įrašant tiesiog skaičių į prievadą (priskiriant PORTA kažkokią reikšmę) yra tikimybė, kad įjungsiu per daug bitų - programos, ypač ilgesnės, metu sudėtinga sekti prievado bitų būseną kiekviename žingsnyje. Tam išvengti galima naudojantis loginėmis operacijomis AND, OR, NOT ir XOR įjungti arba išjungti kiekvieną bitą atskirai.
Aukščiau, apžvelgdamas logines operacijas, nepaminėjau XOR - tai išskirtinis ARBA loginis elementas (Exclusive OR, programavime žymimas ^) - tokios operacijos rezultatas lygus vienetui tik tada, kai abu bitai yra skirtingi; tuo tarpu kai abu bitai yra vienodi, rezultatas bus 0. Štai teisingumo lentelė:
1 bitas2 bitas
1bitas ^ 2bitas
000
011
101
110

Individualiam bito įjungimui ar išjungimui naudojama tokia metodika: imamas esamas prievade (priskirtas) skaičius, jis kažkurios loginės operacijos pagalba sulyginamas su paruošta bitų kauke (bit mask, paaiškinsiu eigoje), rezultatas priskiriamas atgal prievadui. Programavime tai atrodytų taip:
PORTA = PORTA (loginė operacija) [BIT kaukė];
Tiek pasirenkama loginė operacija, tiek bitų kaukė, kuri yra ne kas kita, kaip kitas skaičius parenkama pagal tai, ką norima daryti: įjungti bitą (set bit), išjungti bitą (clear / unset bit) ar tiesiog pakeisti jo būsena, nežinant, kokia ji yra.

Bito įjungimas
Tokiai operacijai naudojamas loginis ARBA. Tarkime, turime bitą, kurio reikšmė nežinoma, o gauti turime bitą, kurio reikšmė - loginis 1. Žiūrimė į teisingumo lentelę:

1 bitas2 bitas1bitas | 2bitas
000
011
101
111
Laikykime, kad turimas bitas yra 1 bitas, o tokios loginės operacijos rezultatas - dešiniajame stulpelyje. Esamą bitą turime loginiu būdu sudėti (primenu, kad operacija ARBA yra loginė sudėtis) su kažkokiu bitu, kad rezultate gautume 1. Jeigu mūsų nežinomasis bitas yra 1, rezultate gausime vienetą bet kokiu 2 bito atveju. Tačiau jei 1 bitas yra nulis - rezultate vienetas bus tik vienu atveju, kai pridedamas bitas (2 bitas) bus lygus vienetui. Taigi, norėdami įjungti bitą, turime loginės sudėties būdu sudėti nežinomą bitą ir vienetą. Tas antrasis bitas ir yra vadinamas bitų kauke, nes jis apsprendžia, kurie bitai bus įjungti, kurie - palikti nepakeisti.
Bitai, kurie kaukėje yra įjungti, bus pakeisti į vienetą (įjungti) pradiniame skaičiuje;
Bitai, kurie kaukėje yra išjungti, pradiniame skaičiuje bus nepaliesti.

Pavyzdys:
Skaičius:   0b01010101
Kaukė:      0b00001000
Rezultatas: 0b01011101

Programavime tai atrodys taip:

PORTA =  0b01010101; // prievadui priskiriamas skaičiusPORTA = PORTA | 0b00001000; // prievado reikšmė sudedama su kauke 



Bito išjungimas
Bito išjungimui naudojama operacija IR, t.y. loginė daugyba. Nesigilinsiu tiek, kiek apie bito įjungimą - viskas labai analogiška, tik kaukė kiek kitaip sudarinėjama:
Bitai, kurie kaukėje yra įjungti, pradiniame skaičiuje bus nepaliesti;
Bitai, kurie kaukėje yra išjungti, bus pakeisti į nulį (išjungti) pradiniame skaičiuje.
Pavyzdys:
Skaičius:   0b01010101
Kaukė:      0b00011000
Rezultatas: 0b00010000

Kaip matome, nepaliesti liko tik 3 ir 4 bitai, visi kiti tapo nuliais.
Programavime:

PORTA =  0b01010101; // prievadui priskiriamas skaičiusPORTA = PORTA &  0b00011000; // prievado reikšmė sudedama su kauke


Labai dažnai patogiau nurodyti, kuriuos bitus išjungti, o ne kuriuos palikti nepaliestus, todėl daroma loginė inversija:
Skaičius:   0b01010101
Kaukė:      0b11100111
NE kaukė:   0b00011000
Rezultatas: 0b00010000

Kaukė tokiu atveju parenkama nurodant tuos bitus, kuriuos reikia išjungti, tada invertuojama ir tik tada sudauginama su pradiniu skaičiumi:

PORTA =  0b01010101; // prievadui priskiriamas skaičiusPORTA = PORTA &  ~(0b11100111); // prievado reikšmė invertuojama ir sudedama su kauke


Ir paskutinis, Bito pakeitimas
Tam naudojama kiek aukščiau paminėta loginė operacija XOR, programavime žymima ^.
Bitai, kurie kaukėje yra įjungti, bus pakeisti pradiniame skaičiuje;
Bitai, kurie kaukėje yra išjungti, pradiniame skaičiuje nepasikeis.

Pavyzdys:
Skaičius:   0b01010101
Kaukė:      0b00011000
Rezultatas: 0b01001101

Programavime:

PORTA =  0b01010101; // prievadui priskiriamas skaičiusPORTA = PORTA ^ 0b00011000; // prievado reikšmė invertuojama ir sudedama su kauke 



Pabaigai
Yra ir kitų būdų pakeisti individualius bitus (pvz, bitų postūmis, bit shifting), tačiau šiam kartui užteks ir tiek. Mikrovaldiklių prievadų valdymą geriausia įsisavinti susijunginėjant mikrovaldiklį realiai, pavyzdžiui, ant maketinės plokštės, ir bandyti pajunginėti prijungtus šviesos diodus.

Komentarų nėra:

Rašyti komentarą