PIC Opg 4 Løsning
Denne side gennemgår løsningen for PIC Opgave 4 - Kombinatorik
Løsningsforslag for Kombinatoriske opgaver.
Generelt er alle løsningerne lavet med den sædvanlig opstartskode til en PIC16F684, og selve funktionen realiseret i et forever loop.
Opstartskoden kan ses generelt gennemgået i
Koden som jal-filer
Alle eksemplerne kan hentes i denne ZIP-fil.
Den binære Adder
Addition af tal sker mange steder, men specielt inde i en mikroprocessor.
Denne opgave præsenterer princippet i det.
Den 1 bits binære full-adder
Til den simple adder anvendes to bit-indgange (de to bits der skal adderes) og en udgang med resultatet og en udgang med carry. Til en full adderskal der også en carry-indgang til.
Indgange og udgange til programmet defineres som følger:
porta_direction = all_input alias bit0 is pin_a0 -- De 2 bit der skal adderes alias bit1 is pin_a1 alias carry_in is pin_a2 -- Carry ind fra inputtet portc_direction = all_output alias res0 is pin_c0 -- Resultatet på den bit der kommer ud alias carry_out is pin_c3 -- Carry ud på resultatet
I programmet udregnes resultatet af half-adderen som et mellemresultat. Dette resultat placeres i en bit, der defineres som følger:
var bit res_ha -- mellemresultat, der er half adder-sum
Hele koden inde i forever-loopet ser ud som følgende:
37 res_ha = bit0 ^ bit1 -- Først regnes en half-adder 38 carry_out = (bit0 & bit1) | (res_ha & carry_in) -- så beregnes carry_out 39 res0 = res_ha ^ carry_in -- og derefter selve resultatet på full-adderen
Linje 37 beregner summen af de to indgangsbit, og gemmer det i den interne variabel.
Linje 38 beregner carry out ud fra begge indgangsbit, mellemresultatet og den carry der kommer ind - resultatet placeres direkte på udgangen.
Linje 38 beregner summen af både de to indgangsbit og carry in - igen lægges det direkte ud på resultat-udgangen.
Koden ligger som opg-4a-full-add.jal i denne ZIP-fil.
2x3 bits full adder
Ideen med full-adders er at de kan lægge større tal sammen, og for at illustrere det, så er der her realiseret en full-adder der tager en carry id og 2 stk. 3 bits tal ind, og lægger dem sammen til et 3 bits tal samt en carry ud.
Indgange og udgange til programmet defineres som følger:
porta_direction = all_input alias tal1_bit0 is pin_a0 -- De 3 bit der bliver til tal1 alias tal1_bit1 is pin_a1 alias tal1_bit2 is pin_a2 alias tal2_bit0 is pin_a3 -- De 3 bit der bliver til tal2 alias tal2_bit1 is pin_a4 alias tal2_bit2 is pin_a5 portc_direction = all_output alias res_bit0 is pin_c0 -- Resultatet på de 3 bit der kommer ud alias res_bit1 is pin_c1 alias res_bit2 is pin_c2 alias carry_out is pin_c3 -- Carry ud på resultatet -- alias carry_in is pin_c5 -- Carry ind fra inputtet pin_c5_direction = input
Hen gennem de 2 x 3 bit der skal adderes anvendes igen et mellemresultat som er en halfadder. Mellem 1. og 2. fulladder kommer der en carry ud af den første fulladder, som, skal virke som carry ind på den 2. fulladder. Til dette anvendes en intern variabel som mellemresultat. Den samme variabel kan anvendes mellem 2. og 3. fulladder.
var bit res_ha -- mellemresultat, der er half adder-sum var bit carry_mel -- Mellemresultat på carry
Hele koden inde i forever-loopet ser ud som følgende:
48 -- Først beregnes full-adder på de to lave bit, der giver et output og en intern carry 49 res_ha = tal1_bit0 ^ tal2_bit0 -- Først regnes en half-adder 50 carry_mel = (tal1_bit0 & tal2_bit0) | (res_ha & carry_in) -- så beregnes carry mellemresultat 51 res_bit0 = res_ha ^ carry_in -- og derefter selve resultatet på full-adderen 52 -- Dernæst regnes på de to næste bit, der igen giver et output og en intern carry 53 res_ha = tal1_bit1 ^ tal2_bit1 -- Først regnes en half-adder 54 res_bit1 = res_ha ^ carry_mel -- så selve resultatet på full-adderen 55 carry_mel = (tal1_bit1 & tal2_bit1) | (res_ha & carry_mel) -- og derefter beregnes carry mellemretultat 56 -- Dernæst regnes på de to sidste bit, der igen giver et output og den endelige carry out 57 res_ha = tal1_bit2 ^ tal2_bit2 -- Først regnes en half-adder 58 carry_out = (tal1_bit2 & tal2_bit2) | (res_ha & carry_mel) -- så beregnes carry_out 59 res_bit2 = res_ha ^ carry_mel -- og derefter selve resultatet på full-adderen
Linje 49-51 regner de første to bit sammen med carry ind, og lægger summen direkte på udgangen, mens den carry der kommer ud af det gemmes, så den kan bruges ved beregning af de næste to bit.
Linje 53-55 regner de næste to bit sammen med carryen der ligger som mellemresultat, og lægger summen direkte på udgangen, mens den carry der kommer ud af det gemmes, så den kan bruges ved beregning af de sidste to bit.
Linje 57-59 regner de sidste to bit sammen med carryen der ligger som mellemresultat, og lægger summen direkte på udgangen. Den carry der kommer ud af det placeres direkte ud på carry out.
Koden ligger som opg-4a-bit.jal i denne ZIP-fil.
Binær addition som tal
Ved at betragte indgangene som binære tal, så kan det laves noget enklere. Til dette formål defineres to variabler:
var byte tal1 var byte tal2
Disse to variabler læses ind fra indgangene
Hele koden inde i forever-loopet ser ud som følgende:
46 tal1 = porta & 0b0000_0111 -- Hent det ene 3 bits tal over i tal1 47 tal2 = (porta & 0b0011_1000) >> 3 -- Hent det andet 3 bits tal over i tal2 48 tal1 = tal1 + tal2 -- Læg tallene sammen (det sker som bit i variablerne) 49 if carry_in then 50 tal1 = tal1 + 1 -- Læg carry til, hvis den er der 51 end if 52 portc = tal1 -- output resultatet, hvor carry ligger i det 4. bit
Linje 46 læser det ene 3 bits tal ind, og sikrer ved en AND-maske, at det kun er fra de 3 bit der kommer noget.
Linje 47 gør det tilsvarende, bare med 3 bit der ligger et andet sted i porta. Der anvendes igen en AND-maske, og derefter skiftes de 3 bit ned på position 2, 1 og 0, så det er et tal fra 0 til 7.
Linje 48 lægger de to tal sammen (det kan give 0-14, så carry er nu placeret i bit position 3 inde i tal1
Linje 49-51 lægger en carry-bit til fra carry in, hvis den er der, så tallet nu kan være 0-15.
Linje 52 lægger resultatet ud på portc. De 3 bit i tallet kommer ud på pin_c0 - pin_c2 og carryen kommer ud på pin_c3.
Koden ligger som opg-4a.jal i denne ZIP-fil.
Binær comperator
Denne opgave handler om at sammenligne to tal
Kun sammenligning af tal
I disse to løsninger sammenlignes 2 sæt 3 bit tal, og der sættes 3 udgange ud fra det.
Indgange og udgange til programmet defineres som følger:
porta_direction = all_input -- De to tal der læses ind ligger på hhv. A0-A2 og A3-A5 portc_direction = all_output -- Definition af de 3 udgange alias greater is pin_c0 alias equal is pin_c1 alias smaller is pin_c2
De to binære tal læses ind i 2 variabler:
var byte tal1 var byte tal2
Hele koden inde i forever-loopet ser ud som følgende:
38 tal1 = porta & 0b0000_0111 -- Læs det første 3 bit tal fra A0-A2 39 tal2 = (porta & 0b0011_1000) >> 3 -- Læs det andet 3 bit tal fra A3-A5 40 if tal1 > tal2 then -- Sammenlign de to tal, og sæt 41 greater = high -- de relevante udgange 42 equal = low 43 smaller = low 44 elsif tal1 < tal2 then 45 greater = low 46 equal = low 47 smaller = high 48 else 49 greater = low 50 equal = high 51 smaller = low 52 end if
Linje 38-39 læser de 2 x 3 bit ind i variablerne tal1 og tal2
Linje 40-43 sætter udgange op, hvis tal1 er større end tal2 - de resterende linjer håndterer mindre end og lig med.
Koden ligger som opg-4b.jal i denne ZIP-fil.
Et program der løser det samme kan skrives som:
38 tal1 = porta & 0b0000_0111 -- Læs det første 3 bit tal fra A0-A2 39 tal2 = (porta & 0b0011_1000) >> 3 -- Læs det andet 3 bit tal fra A3-A5 40 greater = tal1 > tal2 41 equal = tal1 == tal2 42 smaller = tal1 < tal2
Her løser linje 40-42 det der blev lavet i en elsif struktur.
Koden ligger som opg-4b1.jal i denne ZIP-fil.
Sammenligning med cascading input
Kigger man på databladet for en binær comperator som digital kreds, så vil den have 3 input mere, der kan påvirke resultatet. Ideen i det er at man kan sammenligne 2 x 8 med to kredse der hver kan håndtere 2 x 4 bit, og man kan ekspandere til det antal bit man ønsker, ved at lægge flere kredse til.
Der skal defineres nogle indgange der tager imod inputtene:
alias in_greater is pin_c3 alias in_equal is pin_c4 alias in_smaller is pin_c5 pin_c3_direction = input pin_c4_direction = input pin_c5_direction = input
Ekspanderingen kan ske to veje - enten at outputtet går til en kreds med mindre betydende bit, eller at de går til en kreds med mere betydende bit.
Ekspandering mod mindre betydende cifre
Hvis outputtet går til en kreds med mindre betydende cifre, så kan koden skrives på følgende måde:
47 tal1 = porta & 0b0000_0111 -- Læs det første 3 bit tal fra A0-A2 48 tal2 = (porta & 0b0011_1000) >> 3 -- Læs det andet 3 bit tal fra A3-A5 49 if in_greater then -- Sæt Greater, hvis det højere ciffer siger Greater 50 greater = high 51 equal = low 52 smaller = low 53 elsif in_smaller then -- Sæt Smaller, hvis det højere ciffer siger Smaller 54 greater = low 55 equal = low 56 smaller = high 57 elsif in_equal then -- Hvis det højere tal siger Equal 58 portc = tal1 > tal2 -- Sammenlign de to tal, og sæt 59 portc = tal1 < tal2 -- udgangene derefter 60 portc = tal1 == tal2 61 else -- Sæt ikke noget hvis det højere ciffer ikke siger noget 62 greater = low 63 equal = low 64 smaller = low 65 end if
Linje 47-48 læser de to tal ind.
Linje 49-56 håndterer hvis det større tal siger større eller mindre.
Linje 57-60 håndterer hvis det større tal siger lig med - her anvendes løsningen fra før.
Linje 61-65 håndterer hvis det større tal ikke indikerer noget fornuftigt, så sættes alle udgange lave, som en fejltilstand.
Koden ligger som opg-4b2.jal i denne ZIP-fil.
Ekspandering mod mere betydende cifre
Hvis outputtet går til en kreds med mere betydende cifre, så kan koden skrives på følgende måde:
47 tal1 = porta & 0b0000_0111 -- Læs det første 3 bit tal fra A0-A2 48 tal2 = (porta & 0b0011_1000) >> 3 -- Læs det andet 3 bit tal fra A3-A5 49 greater = tal1 > tal2 -- Sammenlign de to tal, og sæt 50 smaller = tal1 < tal2 -- de relevante udgange 51 if tal1 == tal2 then -- tag fra cascading input, hvis tallene er ens 52 greater = in_greater 53 equal = in_equal 54 smaller = in_smaller 55 end if
Linje 47-48 læser de to tal ind.
Linje 49-50 laver sammenligningen på større end eller mindre end.
Linje 51-55 håndterer hvis de to tal er ens, så sendes cascading input videre til næste ciffer, eller ud som resultat.
Koden ligger som opg-4b3.jal i denne ZIP-fil.
Dekoder 3 bit binær til 8
Dekoderen tager 3 bit ind, og fra det tal de danner sættes en udgang, så kombinationerne 0-7 kommer ud på 8 udgangen, hvor den første bliver høj, når der kommer 0 ind, den næste bliver høj når der kommer 1 ind osv.
Indgange og udgange til programmet defineres som følger
porta_direction = all_input portc_direction = all_output
Her anvendes de 3 lave bit i porta til tallet ind, og hele portc er udgangene
Der anvendes 2 variabler. tal1 indeholder det tal der læses ind, og tal2 anvendes til at behandle det der skal ud på udgangen.
var byte tal1 var byte tal2
Løsning med for-løkke
I den første løsning, der anvendes en for-løkke til at finde den rigtige udgang.
Hele koden inde i forever-loopet ser ud som følgende:
32 tal1 = porta & 0b0000_0111 33 tal2 = 0b0000_0001 34 for tal1 loop 35 tal2 = tal2 * 2 36 end loop 37 portc = tal2
Linje 32 læser tallet ind fra porta ind i tal1 - tallet begrænses til 3 bit, så det kun kan være 0-7.
Linje 32 sætter det tal op der skal ende med at komme ud på udgangen - det starter med at ligge med en høj i bit 0, så hvis der ikke ændres på det, så er det pin_c0 der går høj.
Linje 34-36 er det for-loop der sætter bittet hen på den ønskede udgang. Loopet gennemløbes det antal gange der er i tal1, og i linje 35 rykkes bittet en tak ad gangen, det kan gøres ved at gange tallet med 2, eller man kunne også have anvendt en skifte-operator << 1 til det.
I linje 37 lægges resultatet ud på porten.
Koden ligger som opg-4c.jal i denne ZIP-fil.
Løsning med skift af bit
I stedet for at anvende et for-loop, så kan man bruge skiftefunktionen direkte.
Hele koden inde i forever-loopet ser ud som følgende:
32 tal1 = porta & 0b0000_0111 33 tal2 = 0b0000_0001 34 tal2 = tal2 << tal1 35 portc = tal2
Linje 32 læser tallet som før.
Linje 33 sætter tallet der skal skiftes op.
Linje 34 laver selve skifteoperationen, hvor tal2 skiftes fra 0 til 7 gange. Hvis det skiftes 0 gange, så bliver det som det oprindelige, og pic_c0 sættes høj. Hvis der skiftes 7 gange, så skulle det være pin_c7 der bliver sat høj, men da en PIC16F684 ikke har dette ben, så er alle 6 bit der er i porten bare lave. Hvis man vil teste de to sidste bit i portc, så skal man vælge en anden PIC, som f.x. en PIC16F690 der har 8 bit i portc.
Linje 35 er der hvor tallet faktisk lægges ud på porten.
Koden ligger som opg-4c2.jal i denne ZIP-fil.
Løsning med reducering ved skift af bit
Dette er blot en reduktion af det ovenstående eksempel, hvor der er sparet en variabel (tal2) væk, og tingene bliver gjort mere direkte.
Hele koden inde i forever-loopet ser ud som følgende:
31 tal1 = porta & 0b0000_0111 32 portc = 0b0000_0001 << tal1
Linje 31 læser tallet som før.
Linje 32 udfører skifteoperationen, og lægger resultatet ud på portc.
Koden ligger som opg-4c3.jal i denne ZIP-fil.
Koden kan også skrives sammen på en linje, hvor man slet ikke bruger en variabel som vist her:
31 portc = 0b0000_0001 << (porta & 0b0000_0111)
Det sjove er at denne løsning ikke sparer hverken hukommelse eller data-areal, selvom man skulle tro det. Grunden er at den mere komplicerede beregning kræver at den opretter midlertidig hukommelse, og at det kodemæssigt ikke gør nogen forskel, når der alligevel skal bruges midlertidig hukommelse. Denne løsning er ikke med i ZIP-filen.
PIC | |
---|---|
PIC-Typer | PIC16F84 - PIC12F675 - PIC16F628 - PIC16F684 - PIC16F690 - PIC16F877 - PIC18F2550 |
Programmeringsbegreber | Maskinkode - Mikrocontroller |
Programmeringsmiljø | PIC-brænder - UsbPicProg - Analog indgang - ICSP |