Stap 6: Pointers en opzoektabellen
Om het even welk van u die hebben ervaring met C of C++ al hebben ervaring met pointers. We zullen het zelfde ding hier in het kader van "opzoektabellen" gebruiken.
Opzoektabellen zijn een andere manier van het compactifying van onze code om het kortere, meer elegant en gemakkelijker te begrijpen te maken.
Laat eerst de code schrijven en dan zullen we uitleggen wat er gebeurt. Eerst, op de top van ons programma zal er een sectie met het label "nummers:" gevolgd door ".db" assembler richtlijnen. Deze richtlijnen "Definieer bytes" en wat ze doen is dat ze plaatsen die bytes opeenvolgend in een bepaalde sectie van "Programmageheugen" gedefinieerd door de label-nummers. Dus wanneer is de hexadecimale code in de microcontroller geladen, een bepaald segment van het flitsgeheugen dat alle van de programma-instructies worden opgeslagen zal bevatten deze bytes één na de andere in volgorde.
Vervolgens kunnen wij eigenlijk deze nummers krijgen, wanneer we willen dat ze omdat ze altijd gevestigd in bepaalde geheugenlocaties opgegeven programma worden zal. Herinner me hoe we interrupts behandeld? We een instructie aan precies 0x0020 in het programmageheugen geplaatst. We wisten dat als er een timer overflow interrupt is opgetreden voor de cpu zou die exacte locatie controleren en welke commando we er zetten. Opzoektabellen werken goed in een zeer gelijkaardige manier.
We gaan opnieuw schrijven onze "dobbelstenen:" label subroutine, die is enerzijds dat de microcontroller welke pinnen vertelt om welk nummer op een sterven, zodat in plaats van een lange en lelijk sectie van code, kan het gebruik van een lus en dingen eenvoudiger doen inschakelen. Hier is de nieuwe code:
U ziet dat het veel korter is. In feite, wanneer je merkt dat je is herhalende dezelfde set instructies over en binnen een subroutine en het enige ding anders elke keer dat een bepaald aantal, dat is een perfecte tijd verschilt te gebruiken van een opzoektabel. In de gevallen waar zou u een "lus" of een "schakelaar-zaak" routine in C of een andere taal, dat is een goed moment om het gebruik van een opzoektabel in assembler.
Opzoektabellen hebben een reputatie voor het wordt ingewikkeld maar ik denk niet dat het is verdiend. Ik zal proberen uit te leggen op een eenvoudige manier hier.
Laten we beginnen met de atmega328p geheugen kaart. Er zijn drie verschillende soorten geheugen beschikbaar voor het opslaan van spullen. Het "programmageheugen" dat ons programma slaat, de "SRAM datageheugen", die alle van de registers die we als het algemene doel werken registers, de input-output-poorten en alle van de registers die we om de knevel bits en controle gebruiken gebruiken, de manier waarop dingen zijn gedaan en ten slotte het "EEPROM"-geheugen, dat wij in een latere tutorial introduceren zullen bevat (als ik laatste dat lang) en wordt gebruikt voor het opslaan van informatie die niet verdwijnen zal wanneer wij zwenking uit de macht. Erg handig als u een spel maakt en u wilt bewaren somebodies score tot de volgende keer dat ze spelen!
We weten dat elke byte van een bepaalde soort geheugen een adres heeft. Bijvoorbeeld, de eerste byte code wij voeren 0x0000 bedraagt en de timer overflow interrupt handler is op 0x0020, enz. U zult opmerken dat sinds we meer dan 256 bytes van het geheugen in onze programmageheugen ruimte hebben wij niet gewoon adressen 0x00 tot 0xFF gebruiken kunnen. In feite, hebben we 32k flash-geheugen in de geheugenruimte van het programma. Dit betekent dat we moeten adressen uit 0x0000 tot 0x7FFF.
Nu, stel dat we willen om te lezen wat er op een specifiek adres in het geheugen? Bijvoorbeeld, wanneer de cpu een overflow interrupt krijgt het gaat naar 0x0020 en voert de instructie die we daar geplaatst. Wat als we willen plaatsen instructies of gegevens of wat dan ook op een specifiek adres in programmageheugen en vervolgens dat te gebruiken in ons programma? Wij kunnen, behalve dat ons algemene doel registreert kan alleen houden 8 bits (1 byte) tussen 0x00 en 0xFF, en zoals we hebben gezien, een adres heeft 2 bytes te noteren (tussen 0x0000 en 0x7FFF). Er is dus niet genoeg ruimte in een algemene doel-register (d.w.z. een variabele zoals r16) te houden een programma geheugenadres. We kunnen niet "ldi r16, 0b0000000000000010" bijvoorbeeld zeggen aangezien R16 is niet groot genoeg. Dus als we hebben geen manier om het volledige adres hoe op te slaan kunnen gaan we er tijdens het programma? We gewoon pak de telefoon, bel de cpu, en zeggen "kunt u gaan en uitvoeren wat we opgeslagen op 0x2a7f gelieve" je dat adres in een r16 of iets en dan "mov" moet hebben om het of "uit" het vanaf daar.
Dus is hier wat de ATmel mensen hebben gedaan om op te lossen van dit dilemma. Ze hebben dual beoogde doeleinden een paar van onze algemene doel-registers. In het bijzonder, als je kijkt naar de tabel 7-2 op pagina 12 van het gegevensblad, kunt u zien hoe de algemene doel-registers worden georganiseerd. De registers r26, r27, r28, r29, r30 en r31 kan ook worden gecombineerd tot paren genaamd X, Y en Z. Dus dat X r26 en r27 samen is, Y is r28 en r29 samen, en Z is r30 en r31 samen. Op die manier als we Z bijvoorbeeld, de eerste helft van het nemen r30 is en de tweede helft van het r31. Dus als we willen slaan een programma geheugenadres slaan we de helft van het net in r30 en de andere helft van het in r31 en vervolgens vertellen we de cpu Z opzoeken als we willen praten over het hele ding samen. Zij hebben uitgevoerd twee instructies die dit doen. De eerste is spm, wat voor "Winkel programmageheugen staat" en de andere is lpm wat voor "Load programmageheugen staat". Dus nu als we krijgen wat ooit instructie of data wordt opgeslagen op geheugenadres 0x24c8 bijvoorbeeld willen, zouden we dat adres in r30 en r31 en vervolgens wanneer we willen krijgen van de gegevens die we zouden gewoon lpm het aan een variabele door te doen
die zal gaan naar geheugenadres Z, welke gegevens wij er zetten nemen, en plak het in r16. Het koele ding over dit is dat als we er 1 bij tot het gebruik van Z optellen
dan zal Z nu "wijs" het volgende geheugenadres na de eerste. Dus dat als we houden een hele lijst met getallen in het geheugen een na de andere die wij hen door oplopende Z kunt doorlopen.
Hoe gebruiken we dit in ons programma?
Nou, aangezien elk getal op de dobbelsteen wordt weergegeven door het op en uitschakelen van verschillende havens zoals PC2 en PB5 slaan we gewoon het nummer dat voor elk getal op de dobbelsteen doet. Bijvoorbeeld als we "out" 0b11010010 te PortC PC0 wordt ingesteld op 0, 1, PB1 enz en schakelt de bijbehorende LED's om ons te geven onze getal op de dobbelsteen. In dit geval de nummer 4.
Dus we een 'opzoektabel gebruiken zullen' genaamd "nummers:" opslaan van al deze verschillende sterven-configuraties te vereenvoudigen van onze code.
Ik denk dat als je de bovenstaande code leest, en de verschillende aanwijzingen in de handleiding opzoeken, kunt u gemakkelijk uitzoeken hoe het werkt. Het enige vreemde deel is de eerste bit waar we de aanwijzer Z initialiseren.
Wat dit doet is geïnitialiseerd de aanwijzer Z te wijzen op onze lijst met het label "nummers". De reden voor de 2 keer op de voorgrond is dat we willen dat het adres van "getallen" verschoven naar de linker één spatie (thats wat keer door twee doet naar binaire getallen). Dit laat de rechtse bit (de minst significante bits) die vervolgens wordt gebruikt om te beslissen welke byte van programmageheugen verwijzen wij naar gratis. Dit komt omdat programmageheugen 2 bytes (16 bits) breed. Dus bijvoorbeeld, in onze opzoektabel hebben we de eerste twee nummers als
.db 0b01111111, 0b11011110
Aangezien de ruimte programmageheugen 16 bits breed is beide van deze nummers zullen eigenlijk zitten op hetzelfde programmageheugen adres dus de manier waarop we grijpen de eerste of de tweede is waarom we moeten de "times door 2" of linker shift van de bits. Wanneer de "minst significante bit" van Z een 0 is het zal wijzen naar de eerste van onze lijst: 0b01111111, en bij het minste significante beetje van Z is een het naar de tweede men van onze lijst verwijzen zal 1: 0b11011110.
Zoals u zien kunt, verandert het toevoegen van 1 tot en met Z het minst significante bit van een 0 naar een 1 en vervolgens toe te voegen 1 tot en met Z verhoogt opnieuw het programma geheugenadres en de LSB gaat terug naar nul. U ziet dus dat het werkt groot voor het plukken van onze volledige lijst met opgeslagen nummers één tegelijk door simpelweg verhogen van Z.
Bericht dat wanneer we verschuiven het adres van "getallen" links door te vermenigvuldigen met 2 tot het vrijmaken van de minst significante bits gebruiken voor het selecteren van de eerste of tweede byte op dat adres opgeslagen die we verliezen de "meest significante bit" van het adres. Dit betekent dat we alleen onze lookup tabelgegevens kunnen opslaan in adressen waar de meest significante bit niet belangrijk - dat wil zeggen onze benoemde gegevens zullen allemaal de zelfde meest significante bit. Dit betekent dat ons adres is effectief 15 bits lang. 2 ^ 15 is 32768 verschillende adressen beschikbaar voor onze opgeslagen gegevens. We gaan om te kijken naar dit meer in detail in de volgende tutorial dus maak je geen zorgen als het is een beetje verwarrend op dit punt.
Nu weet u hoe opzoektabellen en de X, Y, en Z pointers gebruiken om uw schrijven van de code te vereenvoudigen.
Laten we geven nu het volledige programma met deze vernieuwingen opgenomen.