AVR assembleertaal
Hoe werkt AVR assembleertaal ? Hier in detail uitgelegd (zonder de LOI kom je er ook!)
Wat is een AVR ?
Ten eerste, AVR staat voor Advanced Virtual RISC en/of Alf-Egil Bogen Vegard Wollan RISC, dit zijn de bedenkers van de AVR overigens. Een AVR is een klein IC (ook wel microcontroller genaamd) welke digitaal schakeld (besturen) door middel van i/o's (ingang/uitgang pinnen) De i/o's doen al het schakelwerk. De typen gebruikt op deze webpagina's zijn 8-bit AVR's, wat betekend 1 byte's (1 byte = 8 bits) werk register's, een register is een plek in de AVR waarin je bit's kunt laden en manipuleren, dit doe je met de 118 zogeheten instrukties. De AVR is gebasseerd op de RISC architektuur wat inhoudt dat een instruktie uitvoeren maar 1 klok-cyclus duurt, dit betekend, stel je sluit een 8 MHz kristal aan op de AVR, dan zal de uitvoersnelheid 8 MIPS (miljoen instrukties per sekonde) zijn. De meeste instrukties zijn een klok-cyclus lang (1/klok duurt 1/8.000.000 sek), sommige zijn twee klok-cycli. (dat duurt dus 2/klok's) De instrukties zijn afkortingen van de instruktienamen, ldi betekend bijv.: Load Immidiate, wat betekend; laad een waarde direkt in een register, bijv.:
ldi temp, 0x0A
In dit voorbeeldje laad je het getal 0x0A in het register genaamd 'temp'. 0x0A is A hexadecimaal (= 10 decimaal). Wat houdt dit in ? Je weet dat de registers 8 bit's breed zijn, wil je echt weten hoe het getal 0x0A in dat register zit, converteer het dan naar een binair getal (de processor werk alleen met 1-en en 0-en), dan krijg je dit: 00001010. Hieruit kun je opmaken dat het grootste getal per register maximaal 11111111 (255 decimaal) is. In AVR assembleertaal schrijf je binaire getallen op deze manier: 0b00001010, hexadecimale zo: 0x0A, en decimale gewoon zo: 10.
Hoe laat je een LED knipperen ?
Een AVR heeft een of meerdere Poorten, dit is een adres in de AVR welke de pinnen aan de buitenkant bestuurd, je kan een pin instellen als een ingang of als een uitgang, dit doe je met de Data Direction Register's DDRn (n is A, B, C of D, afhankelijk van welk type AVR je gebruikt), stel je wilt pin 11 van Poort D als uitgang instellen, dan doe je dit:
sbi DDRD, led
Als 'led' 6 zou zijn geweest (PD6), dan zou pin 11 een uitgang geworden zijn (omdat je de 6de bit van het DDRD register gezet had) Nu heb je deze i/o als uitgang ingesteld. Het volgende wat je moet doen is, deze i/o hoog en laag te maken met een bepaalde snelheid. Sluit een LED en serie weerstand (330 ohm) aan op deze pin, zodat je kunt zien wat je programmeerd. Sluit de LED er zo op aan:

Door nu pin 6 van Poort D hoog te maken zal de LED aan gaan, doe dit:
sbi PORTD, led
Dit veroorzaakt dat pin 6 hoog wordt en de LED zal aan gaan (sbi is Set bit in i/o register) Nu vraag je je af hoe heeft ie 'led' 6 gemaakt ? Dat moet je doen door 'led' te defineren, op deze manier:
.equ led = 6 ;LED op PD6
Alles na de ; zal niet worden gezien door de assembler (Een assembler is een programma die het *.asm bestand assembleerd naar een *.hex bestand, welke je vervolgens kan laden in de AVR), dit dient als geheugensteuntje. Schakel nu de LED weer uit door dit te doen:
cbi PORTD, led
Maak bit 6 in PORTD laag. Nu zou je denken, hm als ik nu eens die twee regels na elkaar plaats dan zal de LED dus aan en uit gaan knipperen ? Er zal dus niets gebeuren, omdat de schakelsnelheid van de i/o nog veel te hoog ligt (x-tal = 4 MHz), je moet het schakelen dus heel erg gaan vertragen, d.m.v. een vertragingslus. Een vertragingslus maken is vrij simpel, laat simpelweg een register doortellen van 11111111 naar 00000000 (255 naar 0), dit duurt even. Dit kun je op deze manier doen:

Dit kleine stukje code heeft drie lussen, wat betekend dat je 255 x 255 x 255 keer kan vertragen indien nodig. 'temp' kun je instellen op een bepaald getal, stel je zet 'temp' op 10, dit betekend dan 10 x 255 x 255 als totale vertraging. Vergeet niet dat dat een instruktie gemiddeld een klok-cyclus duurt, dus bij een kristal-frekwentie van 4 MHz zal deze vertraging 49.3 msec * temp worden. Hoezo en waarom 49.3 msec ? Instrukties duren een klok-cycli, vandaar.
2 + temp * 256 * 3 * 256 + temp * 256 * 3 + temp * 3 + 4 = temp * 197379 + 6 klok-cycli (de uitvoertijd van de instrukties) Bij een klok frekwentie van 4MHz, zal dit ongeveer temp * 49.3ms zijn (onthoudt dat bij een frekwentie van 4 MHz is een klok-cyclus 1/4.000.000 van een sekonde!)
Als 'temp' 10 is, dus 10 x 49.3 msec = 493 msec. (1000 msec = 1 sec), dan dus ongeveer 0.5 sekonden vertraging. Dit is een mooie vertraging om een LED te laten knipperen. Maar hoe werkt een vertragingslus precies ? Maak eerst een register schoon (T1 = 0), verlaag met een (dec T1), kijk dan of dat register gelijk aan 0 is d.m.v. een vertak-instruktie (brne delay_1) brne betekend 'vertak indien ie niet voldoet' (testen op 0), op dit moment is de waarde van T1 op 255, dus zal ie vertakken naar delay_1, verlaag dan weer met een (dec T1 -> 254), enz. enz. totdat T1 0 wordt, dan zal het vertakken naar de label delay_1 stoppen en zal het programma vervolgen. Een AT90S1200 heeft 32 werk register's. Je moet altijd een register defineren voordat je die kan gebruiken. (.def T1 = r1), wat ook belangrijk is is welk register je gaat gebruiken, omdat nl. sommige instrukties niet kunnen worden uitgevoerd op de lagere register's 1 t/m 15 (bij een AT90S1200), dus moet je een hoge register defineren, zoals 'temp', die is gedefineerd als r19 in dit voorbeeld. Klik hier voor de gehele code van een LED-knipper programmaatje.