BASIS MICROCONTROLLERS INLEIDING EN ADDENDUM BIJ HET AduC800 DATABOEK.
ROGGEMANS M. 07/2014
2
Inhoud INLEIDING:...................................................................................................................................................... 4 HOOFDSTUK 1: DE UNIVERSELE VERWERKINGSEENHEID .................................................. 8 1.1 INLEIDING: ................................................................................................................................................ 8 1.2 HET GEHEUGEN: .................................................................................................................................... 11 1.3 CPU BUSSEN:........................................................................................................................................... 12 1.4 REGISTERS: ............................................................................................................................................. 15 1.5 INTERRUPS: ............................................................................................................................................. 16 1.6 DE CPU KLOK: ......................................................................................................................................... 17 1.7 DE LABO-OPSTELLING: ........................................................................................................................ 20 1.7.1 INLEIDING:....................................................................................................................................... 20 1.7.2 DE PC: ............................................................................................................................................... 21 1.7.3 DE CONTROLLERKAART: ............................................................................................................... 21 1.7.4 HOGERE<>LAGERE TALEN: ......................................................................................................... 22 1.8 DE INSTRUCTIESET: .............................................................................................................................. 23 1.8.1 INLEIDING:....................................................................................................................................... 23 1.8.2 PROGRAMMAGEHEUGEN <> DATAGEHEUGEN: ...................................................................... 23 1.8.3 REGISTERSTRUCTUUR: .................................................................................................................. 25 1.8.4 BIT ADRESSERING: ......................................................................................................................... 26 1.8.5 VERKORTE INSTRUCTIES: ............................................................................................................ 28 1.9 AANRADER: ............................................................................................................................................ 29 1.10 REFERENTIES NAAR WEBSITE: ........................................................................................................ 30 HOOFDSTUK 2: DE INSTRUCTIESET ................................................................................................ 31 2.1 INLEIDING: .............................................................................................................................................. 31 2.2 PROGRAM STATUS WORD: .................................................................................................................. 32 2.3 ADRESSERINGSMETHODEN: ............................................................................................................... 41 2.4 WISKUNDIGE INSTRUCTIES: ............................................................................................................... 49 2.4.1 INLEIDING:....................................................................................................................................... 49 2.4.2 ADD(C): ............................................................................................................................................. 53 2.4.3 SUBB: ................................................................................................................................................ 54 2.4.4 INC/DEC:........................................................................................................................................... 56 2.4.5 INC DPTR: ......................................................................................................................................... 57 2.4.6 MUL AB: ............................................................................................................................................ 57 2.4.7 DIV AB: .............................................................................................................................................. 58 2.4.8 DA A: ................................................................................................................................................. 58 2.5 LOGISCHE INSTRUCTIES:..................................................................................................................... 59 2.5.1 INLEIDING:....................................................................................................................................... 59 2.5.2 ANL/ORL/XRL: .................................................................................................................................. 59 2.5.3 SPECIFIEKE ACCUMULATOR INSTRUCTIES: ............................................................................. 60 2.6 VERPLAATS INSTRUCTIES: ................................................................................................................. 62 2.7 BIT MANIPULATIES: .............................................................................................................................. 65 2.8 PROGRAMMA EN MACHINE CONTROLE: ......................................................................................... 68 2.9 DRIVERS, LED’S EN SCHAKELAARS: ................................................................................................. 71 2.10 TIPS EN TRUCS: .................................................................................................................................... 73 2.10.1 INLEIDING:..................................................................................................................................... 73 2.10.2 SOFTWARE TIJDSVERTRAGING: ................................................................................................. 74 2.10.3 AANMAKEN EN UITLEZEN VAN TABELLEN: .............................................................................. 76 2.10.4 EEN TABEL GEBRUIKEN OM TE VERTALEN: ............................................................................ 79 2.10.5 HEX, BCD, BIN INLEZEN EN UITSCHRIJVEN: ........................................................................... 81 2.10.6 OMREKENING HEX NAAR BCD: .................................................................................................. 84 2.10.7 OMREKENING BCD NAAR HEX: .................................................................................................. 87
3 2.10.8 VERMENIGVULDIGEN EN DELEN VAN GROTE GETALLEN:................................................... 89 2.10.9 VIERKANTSWORTEL BEREKENEN: ............................................................................................. 95 2.11 REFERENTIES NAAR DE WEBSITE: .................................................................................................. 97 HOOFDSTUK 3: INPUT/OUTPUT ................................................................................................................. 98 3.1 INLEIDING: .............................................................................................................................................. 98 3.2 PARALLELLE I/O: ................................................................................................................................... 99 3.3 SERIELE INTERFACE: .......................................................................................................................... 104 3.4 TIMERS: .................................................................................................................................................. 111 3.5 ANALOOG NAAR DIGITAAL OMVORMER: ..................................................................................... 117 3.6 FAIL SAVE MECHANISMS: ................................................................................................................. 123 3.7 BESLUIT: ................................................................................................................................................ 126 3.8 REFERENTIES NAAR DE WEBSITE: .................................................................................................. 126 HOOFDSTUK 4: INTERRUPT ...................................................................................................................... 127 4.1 INLEIDING: ............................................................................................................................................ 127 4.2 INTERRUPTBRONNEN: ....................................................................................................................... 128 4.3 INTERRUPT PRIORITEITEN:............................................................................................................... 130 4.5 REFERENTIES NAAR DE WEBSITE: .................................................................................................. 136 HOOFDSTUK 5: MICROCONTROLLER TOEPASSINGEN ................................................................... 137 5.1 INLEIDING: ............................................................................................................................................ 137 5.2 GALVANISCHE SCHEIDING: .............................................................................................................. 138 5.3 AANSTUREN VAN EEN OUTPUT: ...................................................................................................... 140 5.4 AANSTUREN VAN EEN INPUT: .......................................................................................................... 145 5.5 KEYBOARD INTERFACE: .................................................................................................................... 148 5.6 DISPLAY MULTIPLEXING: ................................................................................................................. 151 5.7 STAPPENMOTOREN: ........................................................................................................................... 153 5.8 DC MOTOREN: ...................................................................................................................................... 157 5.9 LEVEL SHIFTERS VOOR SERIELE COMMUNICATIE: .................................................................... 159 HOOFDSTUK 6: OEFENINGEN EN OPLOSSINGEN ............................................................................... 162 6.1 INLEIDING EN WAARSCHUWING: .................................................................................................... 162 6.2 TE SNEL LOOPLICHT: .......................................................................................................................... 163 6.3 LOOPLICHT MET TIJDSVERTRAGING: ............................................................................................ 163 6.4 LOOPLICHT MET INSTELBARE TIJDSVERTRAGING: ................................................................... 164 6.5 LOOPLICHT VIA TABEL (LENGTE TELLEN): .................................................................................. 164 6.6 LOOPLICHT VIA TABEL (VERBODEN CODE): ................................................................................ 165 6.7 16 BIT HEX TELLER OP SCHERM PC: ................................................................................................ 166 LITTERATUURLIJST ................................................................................................................................ 168
4
INLEIDING: Reeds lang is het de droom van de mens om een automaat te maken die onze fysische en mentale capaciteiten evenaart. Technologisch was, en is dit nog steeds niet evident. De biologische constructies munten uit in efficiëntie en prestatie/volume verhouding. Probeer maar eens in de afmetingen van een vlieg een motor te stoppen die zichzelf kan verplaatsen, herstellen, voeden, reproduceren, oriënteren, ... Mechanisch zijn we gebonden aan de bestaande verspaningsmogelijkheden, beschikbare materialen en hun eigenschappen. Bovendien kan je een mechanische constructie, hoe noodzakelijk ze ook is, maar beperkt laten inspelen op omgevingsparameters. Een voorbeeld hiervan zijn de benzinemotoren met traditionele carburator. De carburator kan reageren op een verandering in motortemperatuur, maar niet over het volledige werkgebied voor een ideale hoeveelheid brandstof zorgen. Om daar aan te verhelpen, wordt een beroep gedaan op de elektronica. Het is een technologie die ons in staat stelt om alle mogelijke aanverwante grootheden te meten, en een uitgangssignaal te produceren, dat hier rekening mee houdt. In de elektronica beschikken we over twee technieken om gegevens te verwerken. De analoge signaalverwerking laat toe om een signaal te behandelen volgens een wiskundige formule die opgebouwd wordt met passieve componenten (weerstanden, condensatoren en spoelen) en actieve componenten (transistoren ,FET's ...) die elk een bijdrage leveren tot de modificatie van het signaal. Elke schakeling wordt ontworpen voor een specifieke toepassing. Als het ontwerp klaar is, moet voor een minimale wijziging, een volledige redesign gebeuren. Zo kan je van een radio niet zonder meer een T.V. maken. Bovendien zijn een aantal noodzakelijke features moeilijk realiseerbaar. Wij denken hierbij aan een gebruiksvriendelijke bediening, mogelijkheid om gegevens op te slaan voor later gebruik, nauwkeurige timing, en de mogelijkheid van de schakeling om zich aan te passen aan veranderingen in het proces (slijtage van een motor). Het zou bovendien eenvoudiger worden, mochten we over een universele schakeling beschikken, die mits een beperkte ingreep, bruikbaar zou zijn voor een groot gamma toepassingen. De OP-AMP is hier een voorbeeld van. Met dit IC is het mogelijk verschillende functies te realiseren, met een minimum aan aanpassing (sommatieversterker, verschilversterker, integrator,…) Nochtans blijft het aantal toepassingen beperkt. Dit is vooral het gevolg van de starre opbouw. De schakeling kan zichzelf niet aanpassen aan een veranderende omgeving.
5 Het gedrag van een analoge schakeling zal bovendien wijzigen door de wijziging in eigenschappen van de gebruikte componenten. Een weerstand zal in waarde veranderen door temperatuur-fluctuaties, maar ook door veroudering of verandering in vochtigheidsgraad. De analoge schakelingen blijven bovendien gevoelig voor stoorsignalen die bvb. via de voedingsspanning de schakeling binnendringen. De programmeerbare digitale technologie heeft deze problemen niet meer. De relatie tussen de ingangsignalen en de uitgangssignalen wordt niet meer bepaald door de componenten die er tussen zitten, maar door een programma dat deze wiskundige relatie bepaalt. (Het versterken van een signaal kan in vele gevallen vervangen worden door het vermenigvuldigen van twee getallen.) Het ontwerpen van de "regeling/sturing" wordt teruggebracht tot het schrijven van een programma en het bouwen van een interface (schakeling die er voor zorgt dat signalen uit de te controleren omgeving door de digitale elektronica verwerkt kunnen worden, en dit zowel voor input als output.). Het programma zal bestaan uit een reeks van instructies, die in een elektronisch/magnetisch geheugen opgeslagen worden. Een universele schakeling (microprocessor) kan dat geheugen uitlezen, en de instructies interpreteren. Mogelijke resultaten van berekeningen worden op poorten (uitgangen) naar buiten gebracht, of opnieuw in het geheugen opgeslagen. Zo wordt een temperatuur of druk omgezet in een binair getal, dat wiskundig verder verwerkt wordt. Bij een aan/uit regeling wordt de meting vergeleken met een ingestelde waarde, waarbij eventueel rekening gehouden wordt met een hysterese. Afhankelijk van het resultaat van deze vergelijking wordt een uitgang aan of uit gestuurd. Bij een regelaar wordt de wiskundige formule van de gewenste regelkring uitgerekend, en de uitkomst als een analoge waarde (spanning, stroom, PWM) aan een uitgang beschikbaar gesteld. De interface is de link tussen de universele schakeling (microprocessor) en de omgeving. Afhankelijk van de ingangen en uitgangen die er nodig zijn om een sturing/regeling samen te stellen, moet aangepaste periferie gebruikt worden. Zijn er analoge signalen te verwerken, hoeveel digitale inen uitgangen zijn er, .....? Een microprocessor systeem is een universele schakeling, die mits een interface naar het proces, gebruikt kan worden voor het sturen/regelen van gelijk wat. De gebruiker moet alleen een aangepast programma schrijven. Dit programma moet zorgen voor de initialisatie van de periferie, en de relatie tussen de ingangen en de uitgangen. Om dit praktisch uit te voeren zijn er een aantal “tools” nodig: hulpsoftware op een PC en de PC zelf. Een van de doelstellingen van deze cursus is een inzicht te geven in de werking van de centrale verwerkingseenheid en de basis I/O mogelijkheden.
6 De centrale verwerkingseenheid, samen met het elektronische geheugen, vormen de universele schakeling. De “interface” zijn al die elektronische schakelingen die het universele deel aanpassen, zodat communicatie met de omgeving mogelijk wordt. Deze interface zal dan ook afhankelijk zijn van de omstandig-heden waarin de schakeling gebruikt wordt. De basis I/O mogelijkheden zijn volgende interfaces: parallelle I/O (PIO), seriele I/O (SIO), timers/counters, A/D en D/A. In het laboratorium wordt de AduC8xx van ANALOG DEVICES gebruikt. Deze component is gebaseerd op de Intel 8051. Alhoewel de Intel 8051 reeds in 1980 op de markt verscheen, is hij nog steeds populair. Het gros van de 8-bit embedded applications markt wordt door varianten op deze component ingenomen, en ook in de toekomst ziet het er naar uit dat nog gedurende jaren hier geen verandering in komt. Hiervoor zijn tal van redenen terug te vinden. Het is belangrijk voor een fabrikant om als eerste een goed product aan te bieden. Intel is daar in geslaagd met de 40XX, 8080, 8085, 80x86 maar ook met de 8051. Toen de markt behoefte had (of werd de behoefte opgewekt?) aan een bepaalde soort component wist Intel daar wonderlijk op in te spelen. Het gevolg is dat je een markt verwerft, die door verschillende bedrijven als second source wordt aangeboord. Voor een gebruiker is het belangrijk om over een second source te beschikken. Hierdoor wordt je eigen productie onafhankelijk van een bepaalde leverancier. Dit heeft dan weer voor gevolg dat het product voor meer gebruikers aanlokkelijk wordt, met als gevolg dat het marktaandeel toeneemt. Je zal met iets heel goeds op de markt moeten komen om deze vicieuze cirkel te doorbreken. Voor een aantal specifieke toepassingen lukt dit dan ook (uurwerken, rekenmachines,...), maar voor de kleinere reeksen dan weer niet (ABS, elektronische injectie, PLC's, etc....). Hier is het belangrijk dat het ontwikkelen van een toepassing snel kan gebeuren, wat enkel mogelijk is als de technologie niet opnieuw aangeleerd moet worden, en het toepassingsgebied van een IC heel ruim is. De evolutie van het aantal bit dat in een keer kan verwerkt worden is belangrijk in de rekenintensieve toepassingen, waar de kost van de processor ondergeschikt is aan de totale kostprijs van het systeem, of waar de toepassingen rekenkracht vergen. Voor de consumer en industriële embedded toepassingen, is de kostprijs belangrijker dan de rekenkracht van de processor. Dat je rekenmachine er 1 tiende van een seconde langer over doet om een uitkomst te produceren, is minder belangrijk dan de kostprijs van het toestel. Deze wordt door meer bepaald dan de componentprijs. Hier is vooral belangrijk de kost van ervaring en ontwikkeltools. Waarom van processor veranderen, als je de vorige door en door kent, je bovendien alle oude software moet verloren laten gaan, om nog
7 maar te zwijgen over het verlies aan hardware(ervaring)? Zo zien we dat men in de embedded toepassingen nog steeds 4-bit processoren blijft gebruiken en 8-bit gebruikt waar meer rekenkracht en geheugen nodig is. Door een dalende prijs, en aanzienlijk grotere mogelijkheden beginnen de 16-bit en de 32-bit (maar ook de 64/256 bit) controllers hun weg naar meer toepassingen vinden. Het is evident dat de keuze van een controller van heel wat meer factoren afhankelijk is: stokbeheer, beschikbaarheid, kostprijs, rekensnelheid, beschikbare tools,… Tot slot zou ik een aantal redenen willen opsommen die de industrie aanzetten tot de omschakeling van analoge naar digitale verwerking van gegevens:
geen afregelingen (kalibratie gegevens in niet vluchtig geheugen) meer mogelijkheden eenvoudig aanpasbaar (een programmawijziging volstaat) repeteerbaarheid prijs/volume/prestatie verhouding stabiliteit in de tijd eigen fantasie is beperking voor de mogelijkheden fout tolerante werking eenvoudige bekabeling door gebruik van netwerken geen verlies van informatie bij het verzenden van data eigen controle van het toestel ...
Dit wil niet zeggen dat de analoge technieken overbodig worden. Het tegendeel is waar. Bij de interfacing en bij het analyseren van fouten, is de kennis van de analoge technieken onontbeerlijk.
Aan de studenten willen we vooral nog duidelijk maken dat het vak microprocessoren niet iets is, wat je kan van buiten blokken. Het is een manier van denken die je je eigen kan maken door te begrijpen hoe alles in elkaar zit en werkt. Veelvuldig herhalen van de leerstof, en het zelfstandig werken in het laboratorium zijn daarom een noodzaak....
8
HOOFDSTUK 1: DE UNIVERSELE VERWERKINGSEENHEID
1.1 INLEIDING: Zoals reeds in de inleiding werd aangehaald, is het de bedoeling om een hardware schakeling te bekomen die voor meerdere toepassingen gebruikt kan worden, zonder dat er wijzigingen noodzakelijk zijn. Dit is enkel mogelijk als de werking van de schakeling niet door de componenten zelf, maar op een andere manier wordt bepaald. Een mechanisch voorbeeld hiervan is het draaiorgel. Ongeacht de melodie die het moet produceren, blijft de hardware dezelfde. Een drager (geheugen) zal de informatie (programma) bevatten die door het draaiorgel verwerkt moet worden. De drager is in dit geval een ponsband, en het programma (de muziek) bepaalt in welke volgorde de verschillende ventielen aangestuurd worden. Dezelfde techniek wordt ook toegepast bij weefgetouwen. Het patroon van het tapijt wordt nu bepaald door de informatie op een ponsband. De informatie of programma wordt software genoemd. Je kan de machine gaan opsplitsen in drie delen: de drager van de software (geheugen) de eenheid die de drager uitleest (verwerkingseenheid) de ingangen en uitgangen van de stuureenheid (interface of I/O) Deze mechanische constructie werd elektronisch nagebouwd. Hierdoor is het mogelijk om een hogere verwerkingssnelheid te bekomen, maar vooral om het aantal mogelijkheden uit te breiden. Zo is het mechanisch niet eenvoudig om op ingangssignalen een berekening uit te voeren. De instructieset (verzameling van alle bevelen die door een verwerkingseenheid kunnen uitgevoerd worden) van de elektronische schakeling, bevat dan ook een aantal wiskundige instructies. Ook het opslaan van gegevens in het geheugen is mechanisch niet evident, om nog maar te zwijgen over een flexibele "HUMAN-MACHINE INTERFACE". De elektronische verwerkingseenheid noemen we de central processing unit (CPU). Wordt deze CPU als een IC op de markt gebracht, dan noemen we het IC kortweg X CPU (bvb 80486 CPU). Wordt de CPU samen met andere componenten in een IC gestopt, dan spreken we van een CPU_CORE (een bepaald type CPU als kern van een complexere bouwsteen). In de meeste gevallen noemen we zo'n complexe component een microcontroller. Wij gaan de AduC8xx microcontroller behandelen, die een 8051 CPU-CORE bevat. Alle 8051 compatibele microcontrollers gebruiken dan ook een 8051 CPU-CORE, wat wil zeggen dat de verwerkingseenheid steeds dezelfde manier van werken heeft, maar ook dat de
9 instructies die door de CPU verwerkt kunnen worden steeds dezelfde zijn. In de verdere tekst gebruiken we de benaming CPU voor de verwerkingseenheid, ongeacht of ze als een afzonderlijk IC, of als een CORE gebruikt wordt. (MCS51 = 8051 COMPATIBEL) Het is de CPU die, als een automaat, de instructies uit het geheugen zal ophalen, interpreteren en uitvoeren. Afhankelijk van de aard van de instructies, moeten er berekeningen uitgevoerd worden, of worden gegevens van de ene naar de andere geheugenplaats verplaatst. Bij een microcontroller kan dit geheugen "ON CHIP" (in het IC) zijn, of EXTERNAL MEMORY (uitwendig) zijn. Blijft de informatie passief opgeslagen in de geheugenplaats, dan spreken we, afhankelijk van waar de geheugenplaats zich fysisch bevindt, van registers (in de CPU) of van geheugen (memory, on chip of external). Het opvragen van gegevens uit het geheugen duurt langer, dan bij registers. De informatie die in een geheugenplaats opgeslagen is, kan daar niet veranderd worden, maar moet eerst naar een CPU register verplaatst worden. Hoe meer registers een CPU bevat, hoe vlotter gegevens verwerkt kunnen worden. Bij controllers is het aantal registers meestal aanzienlijk (64 tot enkele duizenden), waar dit bij een processor beperkt is tot enkele tientallen. Als de informatie niet passief opgeslagen wordt, maar andere componenten (tellers, analoog naar digitaal omvormers, ...) activeert, dan noemen we die geheugenplaats met bijhorende hardware, een input/output (I/O) bouwsteen. De geheugenplaats zelf, wordt ook wel controleregister (SFR) genoemd. Alhoewel de CPU-CORE in een complexere bouwsteen zit, zal je in het IC steeds een bepaalde basisstructuur terug vinden: CPU (verwerkingseenheid) MEMORY (drager van het programma of gegevens) I/O (inlezen of uitsturen van bevelen van/naar de omgeving) Op de volgende blz. is het blokschema van de AduC800 afgebeeld. Het aantal bit van een CPU is het aantal bit, dat in een keer in de meeste berekeningen verwerkt kan worden. Een 8-bit CPU (processor) kan getallen van 8 bit in één keer verwerken. De minimale waarde is 0, de maximale waarde is 2**8-1=255 decimaal of FF hexadecimaal (FFH). Wenst de gebruiker kleinere getallen te verwerken, dan zal binnen de BYTE (dit is een 8 bit getal) een aantal bit op 0 blijven staan. In het geval dat de berekeningen op getallen van meer dan 8 bit moeten gebeuren (waarde van het getal is groter dan 255D), zal het getal een veelvoud van 8 bit zijn, en moet nagegaan worden hoe de berekening opgesplitst kan worden in
10 sub berekeningen van 8 bit. Hoe groter het aantal bit van de processor, hoe groter de getallen die in een keer verwerkt kunnen worden, hoe sneller de processor meestal zal zijn. Meestal, want als de opdracht niet reken intensief is, zal het voordeel van een "grotere" processor relatief zijn. Bovendien is de “aard” van de getallen belangrijk: floating point, integer, two’s complement,….
De 8051 is een 8-bit CPU core, die individuele bits kan aanpassen, op bytes kan rekenen, en slechts heel beperkt woorden (16 bit) kan verwerken. De volledige structuur van de CPU is dan ook afgestemd op het verwerken van bytes, zowel voor de instructies als voor de data.
11 De verdere tekst is uitsluitend voorzien voor de 8051 core, en de ADuC8xx microcontroller. Bepaalde principes zijn enkel voor dit type controller geldig, en mogen niet veralgemeend worden naar andere processoren/controllers toe.
1.2 HET GEHEUGEN: De CPU haalt uit het geheugen de instructies die zeggen wat er moet gebeuren, en op welke variabelen (data). Het geheugen zal dus niet enkel instructies maar ook variabelen bevatten. Het geheugen bestaat uit een IC dat een groot aantal bytes opslagruimte heeft. Elke geheugenplaats bestaat uit 1, 4, 8, ... bits. Omdat onze processor byte georiënteerd is gebruiken we geheugen van 8 bit per geheugenplaats. Het transport van gegevens, van en naar het geheugen gebeurt via de datalijnen. Er zijn evenveel data lijnen als er bits zijn per geheugenplaats. Om de verschillende geheugenplaatsen te kunnen selecteren binnen het IC zijn er adreslijnen voorzien. Door op die draden een binaire combinatie aan te leggen wordt een geheugenlocatie geselecteerd. Is er 1 adreslijn, dan kan het IC slechts twee geheugenplaatsen bevatten, zijn het er 8 dan zijn er 2**8=256 geheugenplaatsen, enz. Onze controller gebruikt voor zijn inwendig geheugen 16 adreslijnen. Hierdoor is een maximaal geheugen van 2**16=65536 geheugenplaatsen mogelijk. De capaciteit wordt uitgedrukt in kilobytes. 1 Kilobyte is 2**10 0f 1024 bytes groot. In het geval van de ADuC8xx is dus een geheugen van 64kByte mogelijk. Naast de datalijnen en de adreslijnen beschikt het geheugen ook over een aantal controlelijnen. Deze signalen worden gebruikt om te bepalen of het geheugen informatie moet inlezen en opslaan, of opgeslagen informatie naar buiten moet sturen. Alle controlelijnen hebben namen die hun functie beschrijven, vanuit de CPU bekeken. Zo is het write signaal de lijn waarmee de CPU informatie in het IC kan wegschrijven. Voor de beschrijving van de processor is het belangrijk dat we een verschil maken tussen het read only memory en random access memory. ROM (Read Only Memory) kan enkel door de CPU gelezen worden. Het inlezen van informatie in het IC (programmeren van het geheugen) kan niet door de CPU gebeuren, en moet met een speciaal apparaat (PROM_PROGRAMMER) gebeuren. Als de informatie in het IC zit gaat ze ook niet verloren bij het uitschakelen van de voedingsspanning. Het IC is NON VOLATILE (niet vluchtig). De controle signalen van het IC blijven beperkt tot een OUTPUT ENABLE signaal dat door de processor gebruikt wordt om aan te geven dat het IC informatie op de data lijnen mag
12 plaatsen (is dus ook een read signaal). Uit welke geheugenplaats de informatie komt wordt bepaald door de adreslijnen. Voor het programmeren van het IC in de programmer wordt door de fabrikant uitvoerige informatie voorzien, die sterk afhankelijk is van IC tot IC. RAM (Random Access Memory) staat voor lees/schrijf geheugen. Deze component kan door de processor gelezen en geschreven worden. Dit geheugen is meestal vluchtig, zodat na het uitschakelen van de spanning de informatie in het IC verloren gaat. Als controlelijnen beschikt het IC over een READ signaal, dat dezelfde functie heeft als de output enable van het ROM geheugen. Voor het schrijven wordt het WRITE signaal gebruikt. Omdat benamingen van controle lijnen afhankelijk van de fabrikant kunnen veranderen, is het belangrijk de datasheets van een component te raadplegen alvorens het IC te gebruiken. De ADuC8xx gebruikt inwendig FLASH geheugen als rom (62Kbyte), en beschikt daarnaast ook nog over 2Kbyte RAM on chip.
1.3 CPU BUSSEN: Een bus is een verzameling van geleiders die signalen van een zelfde aard transporteren. De 8 draden die de gegevens van en naar het geheugen vervoeren, wordt de databus genoemd. De geleiders die het adres vervoeren dat aangeeft waar er informatie gelezen of geschreven wordt, noemen we de adresbus. Omdat we moeten kunnen lezen en of schrijven naar het geheugen, zijn er ook controlesignalen nodig, die we de controlebus noemen. Aangezien het de processor is die de controlebus stuurt, hebben alle namen van de signalen betrekking op de functie die ze vanuit de processor krijgen. Zo wordt de read lijn actief als de processor wil lezen, en de write lijn als de processor wil schrijven. DE 8051 core werd oorspronkelijk ontworpen om in een embedded application te werken. Dit wil zeggen dat de core, samen met de noodzakelijke I/O en het geheugen in 1 IC wordt samen gebracht. Aangezien er geen uitwendig geheugen nodig is, zullen de CPU bussen enkel inwendig in het IC gebruikt worden. Dit geheel van functies in 1 IC noemen we een rom microcontroller. Deze componenten waren reeds vlug na het ontstaan van de microprocessor beschikbaar, en worden in veel grotere aantallen gebruikt dan de microprocessoren. De voornaamste reden hiervoor is de eenvoudige printplaat die nodig is om een applicatie op te bouwen. Door de beperkte I/O en geheugen die in de controller aanwezig zijn, kan het toevoegen van externe componenten heel belangrijk worden. Dit is enkel mogelijk bij controllers die de inwendige CPU bussen ook naar buiten kunnen brengen. Bijna alle 8051 controllers laten dit toe. Daarom is er een speciale pin voorzien, waarmee de klant kan kiezen tussen inwendige en/of uitwendige bussen. Inwendige bussen zijn enkel
13 zinvol, wanneer er inwendig ook geheugen aanwezig is. Als er inwendig geen geheugen is, dan spreken we over een romless microcontroller. De aansluitingen die aan de microcontroller voorzien zijn, worden gebruikt om naast de voeding, oscillator en reset, I/O signalen aan te sluiten. Worden de bussen naar buiten gebracht, dan gaat er onvermijdelijk I/O verloren. Op de volgende bladzijden zijn diagrammen weergegeven dat laat zien hoe extern geheugen aangesloten kan worden. De poorten P0, P2 en een deel van P3 wordt gebruikt om extern geheugen met de controller te verbinden.
Bovenstaande figuur laat zien hoe extern programma geheugen gebruikt kan worden.
14
Voor het toevoegen van 64kByte RAM kan bovenstaande schakeling gebruikt worden.
De ADuC832 controller laat toe om 16Mbyte extra RAM te gebruiken (niet voorzien bij een standaard 8051 CPU-core). Om het aantal pinnen dat er voor de bussen noodzakelijk is te beperken, wordt een "MULTIPLEXED BUS" gebruikt. Over dezelfde draden worden dan verschillende signalen naar buiten gebracht. Elke groep signalen krijgt gedurende een bepaalde tijd de beschikking over de bus. Met extra controlesignalen wordt aangegeven welke functie er op de draden aanwezig is. Bij de 8051 worden de 8 data lijnen, en de laagste 8 adreslijnen over
15 dezelfde 8 draden naar buiten gebracht.. Gedurende een BUS CYCLE (de totale tijd die nodig is om een byte van of naar het geheugen te transporteren), worden eerst de adreslijnen op de bus gezet. Door een puls van het controlesignaal ALE (Adress Latch Enable) wordt aangegeven dat dit het geval is. Een externe schakeling moet de adreslijnen dan vasthouden, want even later worden dezelfde lijnen gebruikt als databus (zie ook onderstaande figuur). In het geval dat er 16Mbyte externe RAM gebruikt wordt, zullen de hoogste adreslijnen ook een dubbel gebruik kennen.
1.4 REGISTERS: Een gewone CPU wordt gebruikt in toepassingen die veel geheugen vragen (C.A.D. , VIDEO , ....). De instructieset en de bussen worden hiervoor speciaal aangepast. Juist omdat een communicatie met het geheugen vlot verloopt, is er geen noodzaak om veel registers in de CPU te voorzien. Bij een microcontroller, is het slechts zelden noodzakelijk om grote pakketten gegevens te verwerken. Bij de sturing van een vaatwasmachine, of een regelaar, heb je geen megabytes aan gegevens nodig. Om de schakeling eenvoudig te houden, zal men trachten om zonder extern geheugen te werken. Het is dan wel nodig om over voldoende registers in de CPU te beschikken. Het aantal gewone registers varieert van enkele tientallen tot enkele duizenden. "Gewoon" wil zeggen dat ze gebruikt kunnen worden voor het opslaan van gegevens, maar ook dat er in de registers op de getallen elementaire berekeningen uitgevoerd kunnen worden. De geheugenplaatsen die een I/O werking hebben (SPECIAL FUNCTION REGISTERS), zijn in een gewone computer in speciale IC's ondergebracht. Bij de microcontroller zitten die IC's mee in dezelfde verpakking. Aangezien de controller voor I/O intensieve applicaties wordt gebruikt, worden deze SFR's als CPU registers aangesproken. Hierdoor krijgen de SFR's twee
16 eigenschappen die alleen in een controller mogelijk zijn:
je kan op een SFR berekeningen uitvoeren omdat het CPU registers zijn, kan je ze heel snel wijzigen
Bovenstaande figuur geeft een overzicht van de beschikbare registers.
1.5 INTERRUPS: De eigenschappen van een CPU worden bepaald door :
instructieset registers adresseerbaar geheugen aantal bit dat in een keer verrekend wordt de interruptstructuur
Bij de normale uitvoering van het programma, haalt de processor een instructie uit het geheugen, is die opdracht uitgevoerd, dan wordt de volgende opgehaald, en zo maar door. Sommige van die opdrachten kunnen gegevens ophalen uit I/O modules, maar ook naar I/O modules sturen. Het is echter mogelijk dat de processor een deelprogramma uitvoert, waar een ingang niet ingelezen wordt (bvb: noodstop, alarmdetectie, ...), maar dat het heel belangrijk is om die gebeurtenis niet te missen. Er zijn twee manieren om hiervoor een oplossing te bekomen.
17 Bij POLLING gaat het programma de CPU regelmatig alle belangrijke ingangen laten opvragen. Polling is dan ook tijdsintensief, omdat je programma doorspekt is met het, dikwijls nodeloos, inlezen van gegevens en het nagaan of een bepaalde toestand zich voordoet. Bovendien mogen er geen gebeurtenissen voorkomen, die korter zijn dan de tijdsduur tussen twee leesbeurten (om een instructie uit te voeren heeft de CPU een bepaalde tijd nodig). INTERRUPT is een hardware systeem dat de CPU zal verwittigen dat er een gebeurtenis was, en dat de CPU een speciaal programma moet uitvoeren. Voor elke interruptbron is er de mogelijkheid een ander interrupt programma op te starten. Het is duidelijk dat de gebruiker zal moeten instellen welke gebeurtenis in zijn toepassing een interrupt mag genereren, en welk programma hierbij hoort. Kunnen er verschillende interrupts voorkomen, dan zal een andere hardware schakeling bepalen welke interrupt de voorrang (prioriteits regeling) krijgt, of eventueel een andere interruptroutine (programma dat bij een interrupt hoort) mag onderbreken. Ook hier moet de gebruiker de nodige instellingen maken. Dit instellen (programmeren) gebeurt door met instructies de special function registers te initialiseren (laden van de SFR met een bepaald getal). Een pending interrupt (hangende of wachtende interrupt) is een interrupt die door de detectieschakeling gezien is, maar waarvoor de CPU het bijhorende programma nog niet heeft opgestart. Dit is mogelijk als er geen interrupts enabled (toegelaten) zijn (=disabled) , of als er een interruptroutine met hogere, of gelijke, prioriteit in uitvoering is.
1.6 DE CPU KLOK: Elke processor, en dus ook een microcontroller gebruikt een klok. Dit is een blokgolfvormig signaal, met een bepaalde frequentie. Voor elk type processor, wordt een bereik van frequenties toegelaten. Je kan de klok vergelijken met de motor die in de automaat het geheel aandrijft. Alle inwendige logica van de processor wordt op het ritme van de klok gestuurd, zodat ook de inwendige signalen in relatie staan tot die klok. Dat wordt weergegeven in timing diagramma's. De AduC832 gebruikt een uurwerkkristal van 32768Hz. Een extern kristal van deze frequentie, wordt door de on chip oscillator gebruikt om het blokgolf signaal aan te maken.
18
De controller gebruikt een PLL om deze frekwentie te verhogen. Onderstaande tabel geeft de mogelijke inwendige werkfrekwenties.
Bij het kiezen van een werkfrekwentioe wordt er een afweging gemaakt tussen het stroomverbruik dat we wensen toe te laten en de EMC storing die we genereren. Hoe hoger de snelheid van de klok, hoe sneller de CPU zal werken, hoe meer storing er opgewekt wordt en hoe hoger het stroomverbruik zal zijn. In de figuur op de volgende bladzijde kan je de relatie zien tussen de klok en het uitwendig ophalen van instructies. Om de snelheid waarmee instructies uitgevoerd worden te bepalen, wordt dan ook naar de klok verwezen als referentie. De I/O componenten in de microcontroller gebruiken hetzelfde kloksignaal voor hun werking. Ook hier wordt verwezen naar de kloksnelheid (klokperiode) om de werkingssnelheid te bepalen.
19
20
1.7 DE LABO-OPSTELLING:
1.7.1 INLEIDING: Om in het labo oefeningen te kunnen maken is het belangrijk om goed te begrijpen hoe de configuratie is samengesteld. Er is een permanente wisselwerking tussen de hardware en de software van de PC en de controllerkaart.
PC scherm
ADuC8xx Console Seriele verbinding tussen PC en de AduC832 (COM of USB afhankelijk van gebruikte ADuC bord) klavier
Een microcontroller werkt continu, d.w.z. dat de CPU permanent instructies uit het geheugen ophaalt, en uitvoert. Elke computer werkt op die manier. Als het lijkt alsof het systeem niets doet, dan zal de CPU in realiteit continu nagaan of er geen inkomende informatie is (hangt af van de software). Afhankelijk van die inkomende informatie worden dan andere stukken software gestart. Twee soorten software zijn te onderscheiden. Aan de ene zijde is er het "operating system", en aan de andere zijde de "application software". In de ADuC8xx zit er een BOOTLOADER (ingebouwde systeemsoftware voor het programmeren van het FLASH geheugen en dus geen operating system) en een user programma (application software). Tijdens de reset fase van de controller kan de gebruiker een keuze maken tussen beide programma's. Als het processorsysteem geen geheugen met instructies bevat, bij het inschakelen van de spanning, dan zal de CPU toch (zoals een domme automaat) gegevens uit het geheugen ophalen en als instructies uitvoeren. Aangezien de informatie zinloos is, zullen de gevolgen bij het
21 uitvoeren ervan onvoorspelbaar zijn. Applicatie software is een programma dat de controller een bepaalde taak laat uitvoeren. Bij een PC is een applicatie programma WORD. De studenten leren hoe ze applicatie software moeten schrijven is een essentiële doelstelling van het labo. In de meeste eindtoepassingen is er geen operating system aanwezig (embedded applications) en ook de bootloader zal dan niet meer gebruikt worden. Enkel het applicatieprogramma blijft over.
1.7.2 DE PC: De PC wordt hier gebruikt als hulpmiddel bij het maken van programma's voor de ADuC8xx. Naast een IDE systeem is er op de PC nog een serieel programmeer programma aanwezig. Dit is nodig om het inwendige geheugen van de controller te laden (met behulp van de bootloader). Een programma voor een CPU bestaat uit een reeks getallen in binair formaat. In ons geval zijn die 8 bit groot (1 byte). Een getal van 1, 2 of 3 bytes vormt een instructie. Dat getal wordt ook wel CODE of OPCODE genoemd. Die getallen moeten in het geheugen van de controller staan om uitgevoerd te kunnen worden. Omdat het onbegonnen werk is al die getallen met de bijhorende uitwerking van buiten te leren, wordt er een MNEMONIC gebruikt. Een mnemonic is een 1-woord omschrijving van de opdracht (bvb ADD wil zeggen: tel op). Verder worden een aantal OPERANDS toegevoegd. Een operand geeft aan op welke getallen of registers de bewerking moet gebeuren. De opdracht die de controller moet uitvoeren is nu voor ons leesbaar, maar moet omgezet worden naar de bijhorende bytes code. Met de IDE wordt het programma onder de vorm van mnemonics ingegeven (naam.ASM). De ASSEMBLER zet de mnemonics om naar hexadecimale getallen die door de AduC8xx wel uitgevoerd kunnen worden (naam.HEX). Via een hulpprogramma op de PC (zie labo zittingen) en de bootloader worden de opcodes in de inwendige FLASH opgeslagen.
1.7.3 DE CONTROLLERKAART: De controllerkaart bevat een bootloader die tijdens het resetten actief gemaakt kan worden. Dit laat ons toe om het inwendige FLASH geheugen van de controller te laden met een user programma. Als blijkt dat dit fouten
22 bevat, kan het vervangen worden door een nieuwere uitvoering. De controllerkaart bevat een aantal I/O mogelijkheden die gebruikt kunnen worden: een LCD scherm, 8 LED's, 8 schakelaars en een potentiometer. Voor de aansluitingen verwijzen we naar de laboratorium documentatie.
1.7.4 HOGERE<>LAGERE TALEN: In de informaticalessen maken de studenten kennis met een hogere programmeertaal. Dit soort van taal staat dicht bij de gebruiker. Hij kan zijn probleem omschrijven met een syntax die duidelijk leesbaar is (zoals bij de C-taal!?), en zonder dat hij zich zorgen moet maken over de hardware van de machine die het programma moet uitvoeren. Het is de compiler die deze hogere taal omzet in de instructies die de processor van de computer kan uitvoeren. De compiler doet echter veel meer. Hij zal zorgen dat het programma in het geheugen op de juiste plaats komt te staan, dat er geheugen voor de variabelen vrijgemaakt wordt, dat de instructies van de CPU zo gekozen worden dat er geen conflicten zijn met het gebruik van registers etc. Het is duidelijk dat een compiler niet de meest eenvoudige software is die er bestaat. Omdat de compiler niet weet wat de gebruiker met zijn programma wil bekomen, gebeurt de omzetting naar de taal van de CPU niet steeds even efficiënt. Meestal worden er van de computer ook I/O devices gebruikt (klavier, scherm,...), waarvoor de compiler beroep doet op software die in het operating system zit. REALTIME programmatie is in een hogere taal niet eenvoudig, soms onmogelijk. (Met real time bedoelen we hier heel snelle en interactieve I/O gebaseerd op interrupt.) Voor die toepassingen zal de programmeur stukken software inlassen in de taal van de CPU. Hiervoor is een grondige kennis van de hardware noodzakelijk, maar ook van alle software die in het systeem loopt. De taal van de CPU wordt machinetaal of assembly genoemd. Elke instructie in deze taal kan rechtstreeks door de CPU uitgevoerd worden. Het is de programmeur zelf die moet toekijken hoe het geheugen gebruikt wordt, en hoe de hardware aangestuurd moet worden. Dit vraagt een grondige kennis van het systeem, en alle gebruikte hardware. In een lagere taal ben je baas over de processor, er is geen compiler die bepaalde opdrachten niet zal toelaten. Voor controllers zijn hogere talen alleen zinvol als de inwendige structuur van de controller dit toelaat (stack). Soms bestaan ze ook voor controllers die niet geschikt zijn, maar genereren hoeveelheden code die niet verantwoord zijn. Hierdoor wordt de taak veel trager uitgevoerd. Voor iemand die programma's maakt, en ongeacht het niveau van de taal, is een grondige kennis van de hardware een onmiskenbaar voordeel. Denken we maar aan het toevoegen van I/O aan een systeem, of het
23 nagaan dat een bepaalde taak met een type van computer wel uitvoerbaar is. Een belangrijke doelstelling van deze cursus is dan ook het aanleren van de basis hardware structuren en het programmeren in relatie tot deze hardware in assembler.
1.8 DE INSTRUCTIESET:
1.8.1 INLEIDING: De instructieset van een CPU bevat twee soorten instructies. De eerste soort zal gegevens die ergens in het systeem zitten aanpassen (maken van een som, berekenen van een verschil, bit manipulatie, etc.). De tweede soort verplaatst gegevens in het systeem. Dit lijkt zinloos, maar het maakt een belangrijk deel uit van het programma. Zo worden gegevens ingelezen van een poort, verplaats naar het geheugen of naar een andere poort, ... Alvorens de instructieset uit te diepen, is het dan ook wenselijk om de structuur van de AduC800 in detail te bekijken. Hiermee bedoelen we vooral de geheugenstructuur, en de opbouw van de inwendige registers. Voor gedetailleerde informatie verwijzen we naar de ADuC8xx user's manual. Om de leerstof te kunnen begrijpen is het noodzakelijk veelvuldig de gebruikershandleiding en de labobundel, alsook deze cursus, veelvuldig te bestuderen. Het geheel wordt pas duidelijk als ook de oefeningen in het laboratorium eigenhandig opgelost worden.
1.8.2 PROGRAMMAGEHEUGEN <> DATAGEHEUGEN: Bij de meeste processoren is er slechts 1 geheugen (we bedoelen geheugen dat niets te maken heeft met de CPU registers, het kan zowel ON-CHIP (intern), als OFF-CHIP (extern) zijn) . Dit bevat zowel instructies (CODE/PROGRAM) als de gegevens (DATA) die door het programma verwerkt worden. Bij de 8051 CPU-CORE is dit niet het geval. Hier zijn er twee externe geheugens (ze kunnen ook deels of volledig on-chip zitten). Het ene kan enkel gelezen worden, waardoor het slechts geschikt is voor het bevatten van instructies en parameters die niet veranderen (ROM). Het wordt dan ook code memory genoemd. Het andere kan zowel gelezen als geschreven worden, waardoor het geschikt is om variabelen te bevatten
24 (RAM) en wordt dan ook data memory genoemd. In onderstaande figuur worden de twee gebieden grafisch weergegeven.
De ADuC800 kan meer dan de gebruikelijke 64kByte RAM gebruiken. Dit is speciaal bedoeld om data logging mogelijk te maken. Deze structuur maakt het geheugen complex in gebruik, veroorzaakt soms fouten in de software, en maakt het noodzakelijk dat er speciale instructies bestaan voor het adresseren van de verschillende geheugenplaatsen. Voor beide geheugens wordt dezelfde databus (8 bit) en dezelfde adresbus (16 bit) gebruikt. Het verschil wordt gemaakt met drie controlelijnen.
25 READ (RD) en WRITE (WR) worden gebruikt door de instructies die external RAM gebruiken. Deze controlesignalen komen naar buiten op 2 aansluitingen van poort 3. Is er geen external RAM nodig, dan worden de betreffende instructies niet gebruikt, en kunnen de pinnen als extra I/O gebruikt worden. Wordt het inwendige data geheugen ingeschakeld, dan zullen bij de adressering hiervan de externe signalen niet gebruikt worden. Voor het adresseren van het ROM geheugen wordt het controlesignaal PSEN (program store enable) gebruikt. Dit signaal heeft geen alternatieve functies, wat wil zeggen dat de werking van de lijn steeds dezelfde is (de alternatieve functie van het RD en WR signaal is een I/O pin). PSEN wordt door de CPU gebruikt bij het ophalen van instructies, en door speciale instructies voor het ophalen van gegevens (vaste parameters) uit het ROM geheugen. Dit is ook het geval als het programmageheugen inwendig is zoals bij de ADuC800. Er is dus 64K programma geheugen (ROM/CODE MEMORY) en 16M data geheugen (RAM/DATA MEMORY) adresseerbaar. Voor vele toepassingen is dit veel te veel. In ons systeem werd geen uitwendige RAM voorzien. Enkel de 2kByte on-chip kan gebruikt worden. De RD en WR signalen blijven dan ook als I/O bruikbaar.
1.8.3 REGISTERSTRUCTUUR:
Reeds vroeger werd vermeld dat een controller veel registers heeft. In de bovenstaande figuur, en ook in de handleiding van de ADuC8xx worden ze wel eens internal RAM en SFR (Special Function Register) genoemd. In het geval van de ADuC8xx zijn er dat 128+128=256 bytes internal RAM, en maximaal 128 bytes SFR’s. Elk register heeft een adres (een uniek nummer per register), zodat je in het programma kan bepalen welk register je wil gebruiken.
26
Er zijn 256 adressen voorzien voor al de registers (inclusief de SFR's) zodat de adressen 00H t.e.m. 0FFH gebruikt worden. Er zijn dus meer registers dan adressen. Daarom zijn er ook hier speciale instructies die bepaalde registers kunnen adresseren. Er wordt gebruik gemaakt van directe en indirecte adressering. Bij DIRECTE ADRESSERING staat het adres van het register, waarop de instructie betrekking heeft, in de OPCODE (het hexadecimale getal dat voor de CPU de instructie voorstelt). Bij INDIRECTE ADRESSERING, staat er in de opcode het adres van een ander register, dat als inhoud het adres bevat van het uiteindelijke register dat we wensen te adresseren. Deze adressering lijkt omslachtig, en weinig bruikbaar. Het tegendeel is waar. Indirecte adressering wordt veelvuldig gebruikt voor het zoeken van karakters in een string, of het veranderen van een byte in een blok van variabelen. Het komt overeen met het gebruik van pointers in een hogere programmeertaal. De registers vanaf adres 00H t.e.m. 7FH kunnen volgens beide adresseringen aangesproken worden. De adressen 80H t.e.m. FFH zijn twee maal aanwezig. Een keer als SFR's, de tweede keer als gewone registers. Voor de SFR's wordt de directe adressering gebruikt, voor de gewone registers, wordt indirecte adressering gebruikt. Bij de bespreking van de instructieset komen we hier op terug. 1.8.4 BIT ADRESSERING: Een controller wordt veel gebruikt voor het sturen van apparaten en machines. De meeste I/O kan daar teruggebracht worden tot een klep, een ventiel, een sensor,.... Bij processoren is het enkel mogelijk byte georiënteerde I/O te gebruiken. De meeste controllers kunnen hun I/O aansluitingen pin per pin aansturen. Bij de ADuC8xx is dit ook mogelijk met
27 een aantal bits in de registers (max 256) (zie onderstaande figuur voor de 128 bits in de GPR’s, de overige 128 zitten in de SFR’s).
28
1.8.5 VERKORTE INSTRUCTIES: De lengte (het aantal byte) van de opcode is heel belangrijk. Hoe langer de opcode, hoe meer tijd er nodig is om de instructie uit het geheugen op te halen, hoe groter de uitvoeringstijd. Voor een ROM-controller, is de lengte van de instructie ook belangrijk, omdat de hoeveelheid geheugen beperkt is. Er wordt door de fabrikant dan ook veel aandacht besteed aan het inkorten van de opcodes. Het ideale zijn instructies die slechts 1 byte lang zijn (byte georiënteerde processoren). Dit is onmogelijk voor instructies die het adres van een register bevatten, omdat dit reeds 8 bit is. Om dit op te vangen, krijgen 8 registers een 3 bit adres. Dit laat in de opcode ruimte voor 5 bit die de aard van de instructie omschrijven. Deze 8 registers behoren tot de set van 128 registers die zowel direct als indirect adresseerbaar zijn (zie bovenstaande figuur). Ze behouden ten allen tijde ook hun normale 8 bit adres. De instructies die het 3 bit adres gebruiken, kunnen dus enkel voor die 8 registers gebruikt worden. Omdat het veel gebruikte instructies zijn, had men ze graag laten inwerken op meer dan 8 registers, zonder de opcode te verlengen. Dit kan alleen als er meer bits voor het adres gebruikt worden, waardoor er minder bits overblijven voor de aard van de instructie. Een andere oplossing is dat de extra adresbits niet in de opcode zitten, maar in een SFR. De gebruiker kan die andere bits in de SFR schrijven (met lange instructies), waarna dat deel van het programma een set van 8 registers gebruikt, totdat de waarde van de SFR aangepast wordt. De SFR is het PSW (Program Status Word, zie onderstaande figuur), en bevat twee extra adresbits. Hierdoor werken de verkorte instructies op 4 groepen (BANKEN) van 8 registers. De 32 bytes behouden steeds hun 8 bit adres, maar de instructies met verkorte opcode werken slechts op een set van 8 registers.
29
1.9 AANRADER: De instructieset van een processor uit het hoofd studeren is niet erg zinvol. Je mag altijd de TECHNICAL MANUALS gebruiken bij het programmeren van de microcontroller. Gebruik steeds de lijst met toegelaten instructies bij het schrijven van een programma. Als je de lijst veel gebruikt, ken je al vlug de courante instructies uit het hoofd, en zal je kortere wegen ontdekken voor het programmeren van een opdracht. Dezelfde redenering is van toepassing op de I/O modules van de controller. Wij gebruiken in de school meerdere controllers en processoren door elkaar. Het is onbegonnen werk om ze te memoriseren. Je kan de hersencapaciteit beter gebruiken voor het begrijpen van de technologie, en het aanleren van een denkpatroon, dat toelaat om een probleem gestructureerd aan te pakken. Het eigenhandig maken van de laboratoriumopgaven is dan ook een noodzaak. Het aandachtig volgen in de lessen, en het stellen van vragen is onmiskenbaar een pluspunt. Deze twee raadgevingen zijn echter alleen uitvoerbaar als de leerstof regelmatig bijgehouden wordt. Bij het schrijven van een programma is het belangrijk dat je eerst structureel bepaalt wat het programma moet doen. Het omzetten in instructies is daarna eenvoudig. Een FLOWCHART of een stroomdiagram zijn onontbeerlijke instrumenten bij het programmeren.
30 1.10 REFERENTIES NAAR WEBSITE: Voorgaande tekst is een samenvatting van volgende documenten:
8051\8051general\8051_programmers_guide.pdf 8051\8051general\hardware manual.pdf 8051\ADuC800\datasheets\ADuC832DATASHEET.pdf
Niet alle onderdelen kwamen in dit hoofdstuk aan bod, maar zullen verder behandeld worden.
31
HOOFDSTUK 2: DE INSTRUCTIESET 2.1 INLEIDING: Na het inschakelen van de spanning wordt automatisch een RESET signaal gegenereerd (PowerOnReset). De gebruiker kan dit signaal ook manueel bedienen via een schakelaar. Het signaal activeert in de CPU een elektronische schakeling die alle I/O uitschakelt, en de adresteller (ProgramCounter) op nul zet. Hierdoor wordt de controller in een ruststand geplaatst. Vanaf dit ogenblik start de CPU met het ophalen van de eerste instructie uit het codegeheugen. Die moet op het reset address staan dat 0000H is (De reset vector is 0000h). Aangezien dit ROM geheugen is, moet de gebruiker dit reeds op voorhand met een programma laden. Voor de CPU bestaat een programma uit een reeks van 8 bit getallen. Staat er op het adres 0000H geen zinvolle instructie, dan zal de processor het getal toch als dusdanig verwerken, en onvoorspelbare acties uitvoeren. De bytes die het programma voorstellen worden OPCODE genoemd. Een instructie kan uit 1 tot 3 bytes bestaan. Omdat 8 bit getallen voor ons niet erg leesbaar zijn, worden de instructies van de CPU met woorden en afkortingen voorgesteld. Deze MNEMONICS worden zo gekozen dat ze weerspiegelen wat het gevolg van de instructie is. Het zijn deze mnemonics die we op de PC gaan ingeven met behulp van een editor. De assembler zal ze dan omzetten in opcodes, die via het communicatiepakket in ons controllersysteem belanden (met behulp van de bootloader programma dat in de ROM van de microcontroller zit). Na reset zal dan het gebruikersprograma starten. Als de CPU de eerste instructie heeft gelezen, dan voert de CPU de opdracht uit. Automatisch wordt de adresteller (PROGRAM COUNTER of PC) aangepast, zodat op de adresbus het adres van de volgende instructie geplaatst wordt. De CPU zal ze inlezen, met behulp van het PSEN signaal, ze uitvoeren, en de volgende instructie ophalen, …. Als een instructie de CPU de opdracht geeft om een variabele uit het geheugen op te halen, dan worden de bussen eerst gebruikt voor het ophalen van de instructie uit het code memory, waarna ze door de CPU gebruikt worden om de variabele te verplaatsen. Voor de meeste
32 instructies wordt de uitvoeringstijd vooral bepaald door het aantal BUS CYCLES (tijd nodig voor het verplaatsen van 1 byte data over de bussen) nodig om de instructie te lezen en de eventuele data te verplaatsen. Hoe korter de instructie (aantal bytes opcode zo klein mogelijk) hoe sneller de instructie uitgevoerd kan worden. Voor het gebruik van de CPU registers zijn geen extra buscycli nodig. Het programma zal sneller lopen als er registers gebruikt worden, i.p.v. extern geheugen. Voor de bespreking van de instructieset verwijzen we naar 8051\8051 general\8051_programmers_guide.pdf op de webstek. Volgende paragrafen geven enkele richtlijnen in verband met de aard van de instructies. Hiertoe worden ze in groepen ingedeeld. In de manual worden de instructies in alfabetische orde afgewerkt. Op de webstek staan tal van application notes voor de ADuC8xx controller. Het is raadzaam die ook eens te bekijken. Op het www zijn er talloze sites te vinden die meer info over de 8051 verstrekken.
2.2 PROGRAM STATUS WORD:
De PSW (program status word) is een register dat enkel bit informatie
33 bevat. Dit wil zeggen dat de waarde van de byte niet belangrijk is, wel de individuele toestand van de status bits of vlaggen. Ook andere registers kunnen status bits of vlaggen bevatten, maar alleen de het PSW register bevat een aantal vlaggen die bepaald worden door het resultaat van een berekening. Bovendien worden enkele van de vlaggen als getalwaarde in een volgende berekening verder gebruikt. Er zijn ook speciale instructies die de vlaggen van de PSW gaan testen. In bovenstaande figuur worden de verschillende vlaggen toegelicht. Merk op dat de bits in de PSW een uniek bit-adres hebben. Aangezien de processor over bit-instructies beschikt, kan elke bit getest, veranderd of verplaatst worden. De PSW is een SFR. Volgende figuur geeft weer welke instructies de vlaggen beïnvloeden. Om te weten welke waarde de vlaggen zullen hebben na de berekening, moet je weten wat de getallen zijn die deel uitmaken van de bewerking, maar ook hoe de vlaggen door de betreffende instructie aangepast worden (zie instructieset).
Op de volgende bladzijden vindt u een lijst van alle mogelijke instructies. In de tabellen zijn zowel de mnemonic, functionele beschrijving als de opcode opgenomen. Voor een gedetailleerde beschrijving verwijzen we naar de voorbeeldoefeningen en de uitleg in de les. Mocht dat niet volstaan, dan is er nog steeds 8051_programmers_guide.pdf op de website. In de tabellen op de volgende bladzijden is naast de mnemonic de opcode en het aantal bytes dat de instructie lang is vermeld. Verder kan je terugvinden hoe lang de uitvoering ervan zal duren. Tenslotte wordt verkort weergegeven wat de instructie doet. In de 8051_programmers_guide.pdf staat dit voor elke instructie met een voorbeeld uitgewerkt.
34 De cycle in de tabel komt overeen met 12 klokpulsen. Bij een werksnelheid van 2097152Hz van de controller is de duurtijd van een cycle 5.7 µs. Het is dus mogelijk de looptijd van een programma uit te rekenen.
35
36
37
38
39
40
41
2.3 ADRESSERINGSMETHODEN: Om de instructieset van de controller te begrijpen, moet je weten hoe de CPU variabelen in het systeem kan adresseren. Ter illustratie wordt de MOV (verplaats) instructie gebruikt. Deze instructie verplaatst een byte informatie in het systeem. Algemene vorm van een instructie, met tussen vierkante haakjes een parameter die niet steeds aanwezig is of [OPTIONEEL]: MNEMONIC
[BESTEMMING],[BRON]
MNEMONIC:
de omschrijving van wat de instructie doet. vb: MOV = VERPLAATS ADD = TEL OP,....
BESTEMMING: het register waarop de bewerking wordt uitgevoerd, of de bestemming van het te verplaatsen getal, soms optioneel. vb: 030H = adres register 30H A = de accumulator
42 R0 BRON:
=
verkort adresseerbaar register, ....
het register of het getal dat aangeeft van waar de tweede operand moet komen, of wat de tweede operand is. Deze parameter is niet steeds aanwezig (optioneel).
MOV MOVC MOVX
A Rn DIRECT @Ri @DPTR DPTR C BIT
MOV:
mnemonic voor de verplaats instructie tussen registers of bits mnemonic voor de verplaats instructie met external code memory mnemonic voor de verplaats instructie met external data memory
MOVC: MOVX:
,A ,Rn ,DIRECT ,@Ri ,@DPTR ,#GETAL 8/16 BIT ,@A+DPTR ,@A+PC ,C ,BIT
EERSTE KOLOM: alle mogelijke bestemmingen TWEEDE KOLOM: alle mogelijke bronnen Niet alle combinaties tussen de kolommen zijn mogelijk. Om te weten welke mogelijkheden toegelaten zijn, moet je de lijst van de instructieset raadplegen. Verklaring van bronnen en bestemmingen: A:
accumulator (rekenregister)
Rn:
werkregister huidig geselecteerde bank
direct:
een 8 bit hex getal dat het adres is van een direct adresseerbaar register vb: 15H
@Ri:
i kan 0 of 1 zijn. Die twee registers kunnen gebruikt worden voor indirecte adressering. @ wil zeggen dat indirecte adressering gebruikt wordt.
43 DPTR:
de Data PionTeR is het enige register dat door de CPU als een 16 bit register wordt gebruikt. Het bestaat uit twee 8 bit registers, die afzonderlijk ook gebruikt kunnen worden. Voor sommige instructies worden ze als een geheel gezien. De DPTR is het enige register dat met een instructie aangepast of geladen kan worden als 16 bit register.
@DPTR:
de DPTR is het enige register dat gebruikt kan worden voor indirecte adressering met een 16 bit adres, wat ook de enige manier is om gegevens uit het external data- of code memory op te halen. Dit is een omslachtige werkwijze, aangezien eerst de datapointer geladen moet worden met het adres van een variabele, alvorens via de indirecte adressering de variabele gelezen of geschreven kan worden. Als bron of bestemming in de CPU kan alleen de accu gebruikt worden.
@A+DPTR: idem voorgaande, maar bij de waarde in de DPTR, wordt voor de duur van de instructie, de waarde van de accu opgeteld. @A+PC:
PC staat voor Program Counter of programmateller. Dit 16 bit register wordt door de CPU gebruikt om bij te houden op welk adres de volgende opcode gelezen moet worden. Deze adressering werkt enkel met code memory.
#getal:
Wordt het # teken gebruikt, voor een hex getal, dan wordt het als een numerisch getal gezien, in het andere geval zal de assembler het getal als een adres zien (directe adressering). Het getal is normaal 8 bit, alleen voor het laden van de DPTR, wordt een 16 bit getal toegelaten.
C:
carry vlag in de PSW.
BIT:
elke bit die via bitadressering bereikbaar is.
VOORBEELDEN: MOV A,#3FH
;plaats in de accu het getal 3FH
Het getal dat in de accu moet komen staat expliciet in de instructie. We spreken in dat geval van immediate adressering. Getallen mogen in verschillende notaties ingegeven worden: 30H = het getal 30 HEXADECIMAAL (16 tallig talstelsel)
44 30
=
30O =
het getal 30 in het decimale talstelsel. Het vertaalprogramma (ASSEMBLER) zal dit getal wel eerst omzetten naar hexadecimaal, alvorens het in de instructie te plaatsen. 30 wordt dan 1EH. De omrekening zie je enkel in de opcode, ze is niet merkbaar in de broncode (naam.ASM file), en ook niet in de listing (naam.LST). octaal talstelsel. Verder verloopt de verwerking zoals bij de decimale getallen. Het getal wordt 18H. (Dit talstelsel passen we niet toe.)
000100001B =
binair talstelsel. Dezelfde redenering is van toepassing zoals bij de decimale getallen. Het getal wordt 21H.
Je merkt op dat er voor elk getal een extra 0 wordt ingegeven. De 0 heeft geen numerische betekenis, en is enkel nodig om de assembler een foutloze omzetting te laten uitvoeren. De assembler herkent getallen alleen wanneer ze met een cijfer tussen 0-9 beginnen. Voor hexadecimale getallen die beginnen met de letters A-F is die extra 0 noodzakelijk. Uit gewoonte plaatsen we de 0 er overal bij. MOV A,3FH
;neemt een kopie van de inhoud van het ;register met adres 3FH naar de ;accumulator
Alle adressen tussen 00H en FFH zijn toegelaten. De registers bevinden zich in de controller. Voor de onderste 128 registers is er geen verwarring mogelijk omdat zij slechts een keer in de controller aanwezig zijn. Voor de adressen 80H-FFH zijn er per adres twee registers aanwezig (zie figuur volgende bladzijde). Omdat het adres van het bronregister expliciet in de instructie is opgenomen spreken we van directe adressering. Voor de mogelijke talstelsels die gebruikt mogen worden bij de ingave verwijzen we naar de immediate adressering.
45
MOV A,Rn
;zet in de accumulator de inhoud van ;een verkort adresseerbaar register.
In de figuur op de vorige bladzijde kan je zien dat er 8 registers zijn waarop de verkorte adressering van toepassing is: R0 t.e.m. R7. Ze worden ook wel een registerbank genoemd. De verkorte adressering geeft kortere (en dus ook snellere) opcodes dan andere vormen van directe
46 adressering. In de controller zijn er 4 registerbanken aanwezig. De verkorte adressering is slechts van toepassing op een bank. In de PSW zijn er twee bits die bepalen welke bank dat is. De PSW is opgenomen in onderstaande figuur.
Bovendien zijn er instructies die alleen gebruikt kunnen worden met de verkort adresseerbare registers (R0-R7). Samen met de accumulator en het PSW register vormen deze registers de meest gebruikte registers in een programma.
MOV A,@Ri
;in Ri met i=0 of 1, staat een adres ;dat verwijst naar het eigenlijke ;register dat we wensen te lezen.
Dit type van instructies wordt vooral gebruikt voor het adresseren van tabellen. Het wordt indirecte adressering genoemd omdat het adres van het te lezen/schrijven register niet expliciet in de instructie voorkomt. Wel wordt de plaats aangegeven waar dat adres te vinden is. Er zijn zo maar twee mogelijke plaatsen: R1 of R0 van de huidig geselecteerde registerbank.
47 De vier instructies die werden besproken geven bijna alle mogelijke manieren om gegevens tussen registers te verplaatsen. Om dit te illustreren hebben we MOVE instructies gebruikt. Ook bij de andere instructies zijn dezelfde adresseringsmethoden mogelijk. Bij het maken van een programma kan je best met de instructielijst controleren of de instructie die je wenst te gebruiken wel bestaat. In de volgende drie voorbeelden geven we aan hoe het mogelijk is gegevens van en naar het externe geheugen te verplaatsen. Hier kan enkel indirecte adressering gebruikt worden. Omdat het adres nu 16 bit groot is, kan je R0 of R1 niet meer gebruiken. Die zijn immers maar 8 bit groot. Daarom zijn er in de controller 2 SFR’s beschikbaar die samen een 16 bit register vormen De DPTR bestaat uit de registers DPL en DPH. Ze zijn zowel afzonderlijk als twee 8 bit SFR registers bruikbaar of als een 16 bit SFR. Sommige instructies gebruiken enkel de 16 bit samenvoeging. Die kan je herkennen door dat in de mnemonic de afkorting DPTR gebruikt wordt. Om ze als 8 bit registers aan te spreken kan je al die instructies gebruiken die van toepassing zijn op SFR’s (directe adressering). MOVX
A,@DPTR
MOVX
@DPTR,A
;lezen van het externe RAM ;geheugen. ;schrijven naar het externe RAM ;geheugen.
In beide gevallen bevat de DPTR het adres van de externe RAM locatie die gelezen/geschreven wordt. Het lezen of schrijven naar externe RAM kan enkel via de accumulator gebeuren. MOVC
A,@A+DPTR
;lezen van het code of ;programma geheugen.
Het externe programmageheugen kan enkel gelezen worden via de accumulator. Vermits het een ROM geheugen is, heeft het geen zin er naar te schrijven. Er zijn dan ook geen hardware mogelijkheden om dat te doen. In het labo wordt uitgelegd hoe dat wel kan door de speciale hardware die we extern gebruiken (hierdoor kan er wel minder geheugen gebruikt worden dan maximaal voorzien is). LET OP: in de datasheet van de ADuC8xx staat beschreven dat er twee datapointers beschikbaar zijn. Het controleregister DPCON laat toe om te bepalen hoe beide pointers gebruikt kunnen worden. Aangezien enkel in specifieke gevallen beide pointers gebruikt worden, verwijzen we naar de documentatie i.v.m. de gebruiksmogelijkheden. In de volgende tekst wordt een programma weergegeven zoals het in de editor ingegeven wordt:
48
org
0000h
mov a,#05ah mov 0a0h,a ljmp 0000h #include
;start adres van het ;programma (zie uitleg labo) ;zet in de accumulator het getal 5ah ;kopieer het getal naar poort 2 ;(LED’s) ;sprong naar het startadres van het ;programma
“c:\aducez2.inc”
De vetgedrukte delen in het programma zijn geen instructies maar directieven voor de assembler. Een directief is een aanwijzing die zegt hoe het vertalen moet gebeuren. De laatste lijn zorgt er voor dat een op voorhand geschreven programma aan het programma van de gebruiker geplakt wordt. Hierdoor krijg hje extra mogelijkheden (zie verder). LET OP: Je zal op een of andere manier de controller moeten zinvol bezighouden. Hij stopt immers nooit met het uitvoeren van instructies. In dit programma is daarom de ljmp of Long JuMP instructie gebruikt die de controller zegt dat hij naar het adres 0000h moet springen. In de voorbeeld programma’s die verder in de tekst zijn opgenomen laten we het directief na # weg. Het MOET wel opgenomen worden in elk programma dat je maakt. Volgend directief gaan we ook veel gebruiken: NAAM
EQU
GETAL
Het EQU directief laat toe om aan een NAAM een getal toe te kennen. JEF
equ
32h
;jef=32h
Overal waar we in ons programma het getal 32h willen gebruiken kunnen we nu ook de naam jef plaatsen. Hierdoor is het mogelijk om een programma leesbaar te maken. De equ kan zowel voor 8 bit als voor 16 bit getallen gebruikt worden. In het vorige programma passen we dit toe. LET OP: De assembler is niet hoofdletter gevoelig. JEF==jef==Jef. P2
equ
a0h
;het adres van SFR p2 is a0h
49
Start
equ
00000h
org
start
mov a,#5ah mov p2,a ljmp start #include “c:\reg832.pdf”
;deze lijn mag niet ingegeven ;worden, omdat ze reeds in de :reg832.pdf file opgenomen is ;start adres van het programma ;start adres van het ;programma ;(zie uitleg labo) ;zet in de accumulator het getal ;5ah ;kopieer het getal naar poort 2 ;(LED’s) ;sprong naar het startadres van ;het programma
Door de #include “c:\reg832.pdf” in het programma hoeven we p2 niet meer met een equ directief te definieren. Deze lijn mag (moet want p2 mag geen twee keer een getalwaarde krijgen) dus weg. Alle registers of bits die in het databoek een naam hebben, mogen met die naam in een programma gebruikt worden. Het is niet nodig (toegelaten) om ze met een equ directief te definiëren. Let op: sommige bits kunnen niet rechtstreeks geadresseerd worden, omdat de hardware in de controller dit niet toelaat.
2.4 WISKUNDIGE INSTRUCTIES:
2.4.1 INLEIDING: Deze instructies voeren een berekening uit op een of twee variabelen. Een variabele moet steeds in een register staan. De tweede kan in een register staan, maar kan ook een getal zijn dat in de instructie wordt meegegeven. De 8051 cpu core heeft een accumulator structuur. De ACCU of A register heeft een speciale functie. Enkele belangrijke berekeningen kunnen enkel in de accumulator gebeuren (+, -, *, /, schuiven van bits). Merk op dat de CPU slechts de vier hoofdbewerkingen kan uitvoeren, aangevuld met enkele logische bewerkingen. Het is niet onmogelijk om een sinus of een log te berekenen, je kan immers alle bewerkingen reduceren tot de vier hoofdbewerkingen. De PSW is een belangrijk register bij dit type instructies. Het geeft informatie over de uitkomst van de berekening. Bij sommige bewerkingen
50 kan de uitkomst 9 bit groot zijn. Die negende bit wordt in de PSW tijdelijk opgeslagen, zodat hij in kettingberekeningen verder verwerkt kan worden. De CPU kan enkel met hexadecimale getallen rekenen. Wensen we andere types van getallen te verwerken (BCD, two's complement, ...), dan zullen we ons programma zo moeten schrijven dat er eventuele correcties gebeuren op de uitkomst van de berekening. Zo is 09H+01H=0AH voor de CPU. Wij verwachten als uitkomst 10. Een correctie is dus noodzakelijk. Omdat die correcties niet steeds even eenvoudig zijn, zullen veel programmeurs er de voorkeur aan geven om alle getallen eerst naar hex om te rekenen, ze inwendig te verrekenen, en alvorens ze als output naar buiten te sturen, opnieuw om te rekenen naar het gewenste talstelsel. Laten we nog even de talstelsels overlopen:
Een decimaal getal kan per karakter 10 toestanden weergeven: 0 1 2 3 4 5 6 7 8 9.
Een getal bestaat uit meerdere van die karakters: 189= 1*100 + 8*10 + 9. Hierbij is 10=9+1 ; 100=10*10
Een hexadecimaal getal kan per karakter 16 toestanden weergeven: 0123456789ABCDEF
Een getal bestaat uit meerdere van die karakters: 1B3H= 1*100H + b*10H + 3*1H Hierbij is 100H=10H*10H ; 10H=FH+1H
Het achtervoegsel H (h) wordt gebruikt om aan te geven dat het over een hex getal gaat. Bij decimale getallen wordt geen achtervoegsel gebruikt. Omdat wij graag weten over hoeveel eenheden het werkelijk gaat, rekenen we het hex getal soms om naar een decimaal getal. Hierbij is AH=10 ; BH=11 ; CH=12 ; DH=13 ; EH=14 ; FH=15 10H=FH+1=15+1=16 100H=10H*10H=16*16=256 1000H=100H*10H=256*16=4096
51
Een BCD (binary coded decimal) getal is ook een hex getal, alleen werden de letters A-F niet gebruikt. Voor getallen die bestaan uit een karakter is de numerische waarde voor decimale, hexadecimale en BCD getallen gelijk bij de cijfers 0-9. Zowel bij decimale als BCD getallen zijn de letters A-F niet toegelaten.
Een binair getal bestaat uit een aantal bits. En getal dat bestaat uit een bit kan maar twee waarden aannemen: 0/1. Bestaat het getal uit meerdere karakters, dan kunnen grotere getallen verkregen worden: 11010=1*2**4 + 1*2**3 + 0*2**2 + 1*2**1 +0*2**0
Meestal wordt het achtervoegsel B (b) gebruikt. Het is belangrijk dat er duidelijke afspraken gemaakt worden over het talstelsel waar in gewerkt wordt. Inwendig in een computer bestaan er enkel binaire getallen. Ook al produceert een winkelkassa een bedrag in het decimaal talstelsel, inwendig wordt alles binair verwerkt. Vermits de controller enkel in binair kan rekenen zal het ingegeven getal eerst van decimaal naar binair omgezet moeten worden. Alle totalen worden berekend, waarna de uitkomst opnieuw naar een decimaal getal omgezet wordt. 18=10010B Voor grotere getallen wordt het aantal eentjes en nullen aanzienlijk, waardoor het moeilijk wordt om verbaal getallen door te geven: 511= 111111111B 65535= 1111111111111111B Om dit probleem op te lossen worden getallen in groepjes van 4 bits weergegeven: 511= 0001 1111 1111B 65535= 1111 1111 1111 1111B Om minder karakters te moeten gebruiken, wordt het hexadecimale systeem gebruikt:
52
511= 65535=
0001 1111 1111B = 1111 1111 1111 1111B =
1FFH FFFFH
Dit is dan ook de belangrijkste reden waarom het hexadecimale talstelsel veelvuldig gebruikt wordt. In de tabel zijn ook de BCD voorstellingen opgenomen. Merk op dat er voor elk decimale karakter 4 bits gebruikt worden, en dat de coderingen boven 1001 niet voorkomen. De BCD voorstelling wordt soms gebruikt om decimale getallen inwendig in de computer voor te stellen: 511= 0101 0001 0001B (maar decimaal talstelsel) 65535= 0110 0101 0101 0011 0101B (maar decimaal talstelsel) Een BCD getal ziet er hetzelfde uit als een hex getal, alleen hebben ze een totaal andere betekenis. Inwendig in de computer worden alle getallen binair voorgesteld. Hexadecimaal en BCD zijn voorstellings-vormen die uitwendig gebruikt worden. Wil de computer juiste berekeningen kunnen uitvoeren, is het belangrijk dat we bij het schrijven van het programma rekening houden met het gebruikte talstelsel. Indien niet uitdrukkelijk vermeld gebruiken we steeds HEX getallen.
53
2.4.2 ADD(C): De ADD of tel op instructie laat toe om de som te maken tussen twee 8 bit getallen waarvan er minstens een in de accu moet staan. De uitkomst van de berekening komt steeds in de accu te staan. Omdat de som van twee 8 bit getallen een 9 bit uitkomst kan opleveren, wordt de carry gebruikt als negende bit. Er zijn vier mogelijke manieren om de instructie te gebruiken: ADD A,Rn
tweede operand is R0-R7 van de huidig geselecteerde registerbank
ADD A,direct
tweede operand is een direct adresseerbaar register met een adres 00H-FFH
ADD A,@Ri
het adres van het register dat de tweede operand bevat, staat in R0 of R1 van de huidig geselecteerde registerbank
ADD A,#getal
de tweede operand is een 8 bit getal dat in de instructie staat
De ADDC of ADD with Carry instructie is identiek aan de ADD instructie, behalve dat ook nog de waarde van de carry, zoals die was vlak voor de instructie, mee opgeteld wordt. Hierdoor is het mogelijk om getallen met elkaar op te tellen die groter zijn dan 8 bit. Voorbeeld van een 8 bit optelling: main reg1l reg1h reg2l reg2h
equ equ equ equ equ
0000h 20h 21h 22h 23h
;start adres programma ;cpu registers die we gaan ;gebruiken
org
main
;assembler zeggen waar programma ;start
mov mov add mov
reg1l,#20h a,#57h a,reg1l reg2l,a
;getal 20h in reg1l laden ;getal 57h in accu laden ;tel register op bij accu ;uitkomst naar reg2l
54 ljmp main
;blijf dit doen
Voorbeeld van een 16 bit optelling: main reg1l reg1h reg2l reg2h uitl uith
equ equ equ equ equ equ equ
0000h 20h 21h 22h 23h 24h 25h
;start adres programma ;cpu registers die we gaan ;gebruiken
org
main
;assembler zeggen waar programma ;start
mov mov mov mov
reg1l,#f0h reg1h,#35h reg2l,#60h reg2h,#15h
;getal ;getal ;getal ;getal
f0h in reg1l laden 35h in reg1l laden 60h in reg1l laden 15h in reg1l laden
; we gaan de 16 bit optelling uitvoeren van 35f0h + 1560h mov a,reg2l add a,reg1l mov uitl,a mov a,reg2h addc a,reg1h
;eerst som lage registers ;houden geen rekening met ;carry uit vorige berekeningen ;uitkomst bewaren
mov uith,a
;hoge deel verwerken ;nu moet de carry uit de vorige ;berekening wel gebruikt worden ;uitkomst naar uith
ljmp main
;blijf dit doen
2.4.3 SUBB: De SUBstract with Borrow instructie laat toe om het verschil te maken tussen het getal in de accumulator en een tweede operand. De adressering is identiek aan die van de ADD instructie. e carry heeft een andere betekenis dan bij de optelling. De carry wordt nu op 1 gezet, als de inhoud van de accumulator kleiner is dan de tweede operand. Om de berekening te kunnen uitvoeren, moet de CPU gaan lenen. Dit wordt aangegeven door de carry op 1 te zetten. De instructie zal altijd de waarde van de carry, zoals die was vlak voor de uitvoering
55 van de instructie, als extra aftrekken van het resultaat. Dit is enkel zinvol als we berekeningen van meer dan 8 bit uitvoeren. Het is daarom soms nodig om de carry op 0 te zetten alvorens een verschil te berekenen. Voorbeeld van een 8 bit verschil: main reg1l reg1h reg2l reg2h
equ equ equ equ equ
0000h 20h 21h 22h 23h
;start adres programma ;cpu registers die we gaan ;gebruiken
org
main
;assembler zeggen waar programma ;start
mov mov clr subb
reg1l,#20h a,#57h c a,reg1l
;getal 20h in reg1l laden ;getal 57h in accu laden ;carry op nul zetten ;verminder accu mer inhoud reg1l
; omdat er niet geleend moet worden bij de berekening zal de ; carry nu op 0 staan
end
mov reg2l,a
;uitkomst naar reg2l
ljmp main
;blijf dit doen
Voorbeeld van een 16 bit verschil: main reg1l reg1h reg2l reg2h uitl uith
equ equ equ equ equ equ equ
0000h 20h 21h 22h 23h 24h 25h
;start adres programma ;cpu registers die we gaan ;gebruiken
org
main
;assembler zeggen waar programma ;start
mov mov mov mov
reg1l,#10h reg1h,#35h reg2l,#60h reg2h,#15h
;getal ;getal ;getal ;getal
f0h in reg1l laden 35h in reg1l laden 60h in reg1l laden 15h in reg1l laden
56 ; we gaan het 16 bit verschil berekenen van 3510h - 1560h mov a,reg1l clr c subb a,reg2l mov uitl,a ; ; ; ;
omdat 10h – 60h een negatief getal zou geven zal de cpu een gaan lenen waardoor het verschil van 110h-060h berekend wordt. het gaan lenen wordt aangegeven door de carry te zetten. In een volgende berekening moet de carry verrekend worden. mov a,reg1h subb a,reg2h mov uith,a
; ; ; ; ;
;eerst verschil lage registers ;wensen een vroegere carry niet ;te gebruiken ;houden geen rekening met ;carry uit vorige berekeningen ;uitkomst bewaren
;hoge deel verwerken ;nu moet de carry uit de vorige ;berekening wel gebruikt worden ;uitkomst naar uith
omdat de uitkomst van 3510h-1560h een positief getal is, zal de carry nu op 0 staan. Stel dat de uitkomst negatief zou zijn dan zal de carry op 1 staan. De uitkomst klopt dan slechts gedeeltelijk: 1234h – 1235h = ffff en c=1 dit probleem wordt verder in de labozittingen behandeld. ljmp main
;blijf dit doen
2.4.4 INC/DEC: De INCrement of DECrement instructies verhogen, of verlagen de waarde in het betreffende register met 1. Er worden geen vlaggen aangepast. Voorbeeld: inc inc inc
R0 a 23H
mov R0,#23h inc @R0 dec
R0
;register R0 wordt met een verhoogd ;accumulator wordt met een verhoogd ;register 23H wordt met een verhoogd ;het getal 23H wordt in R0 geladen ;de inhoud van het register, waarvan ;het adres in R0 staat wordt met een ;verhoogd ;register R0 wordt met een verminderd
57 dec dec
a 23H
mov R0,#23h dec @R0
;accumulator wordt met een verminderd ;register 23H wordt met een verminderd ;het getal 23H wordt in R0 geladen ;de inhoud van het register, waarvan ;het adres in R0 staat wordt met een ;verminderd
2.4.5 INC DPTR: Deze instructie is de enige 16 bit bewerking die door de core uitgevoerd kan worden. Ze verhoogd de waarde van het 16 bit register DPTR (dat bestaat uit twee 8 bit registers), met 1. Ook hier worden er geen vlaggen aangepast. Deze instructie wordt vooral gebruikt als de DPTR als pointer gebruikt wordt.
2.4.6 MUL AB: Met deze instructie wordt de inhoud van de accumulator vermenigvuldigd met de waarde van het B register. Het B register is een SFR die enkel bij de MUL en de DIV instructies een speciale functie heeft. Omdat de uitkomst groter is dan 8 bit (max 16 bit), worden A en B gebruikt voor het resultaat. Het B register bevat de 8 hoogste bits van het resultaat, de accumulator bevat de 8 laagste bits. Voorbeeld: mov a,#23H mov b,#4aH mul ab
;getal 23H in de accumulator ;getal 4aH in het b register ;vermenigvuldig
na het programma staat er in de accumulator 1eH en het b register bevat 0aH, wat als uitkomst geeft 0a1eH. Indien a en b een maximale waarde van ffH zouden bevatten, is de uitkomst van de vermenigvuldiging fe01H, wat nog steeds een 16 bit getal is. Er kan dus nooit een overflow optreden.
58 2.4.7 DIV AB: Bij de deling wordt de inhoud van de accu door de inhoud van het B register gedeeld. Bij een deling door 0 wordt de overflow flag geset. De uitkomst van de deling wordt in de accumulator bewaard, in het B register staat de rest van de deling. Voorbeeld: mov a,#23H mov b,#4aH div ab
;getal 23H in de accumulator ;getal 4aH in het b register ;delen
na het programma staat er in de accu 0H en het b register bevat 23H mov a,#4aH mov b,#23H div ab
;getal 4aH in de accumulator ;getal 23H in het b register ;delen
na het programma staat er in de accu 2H en het b register bevat 4H
2.4.8 DA A: Studenten wordt aangeraden deze instructie NIET te gebruiken. Zoals reeds vroeger werd aangehaald, kan de processor enkel rekenen met hex getallen. Laten we duidelijk zijn. Inwendig werkt de controller steeds met binaire getallen. In een vorige paragraaf hebben we gezien dat de hex_notatie slechts een andere vorm is om binaire getallen weer te geven. Soms is het wenselijk om met andere talstelsels te rekenen. De meest voorkomende zijn dan de two's complement en de BCD getallen. De CPU weet niet welk type van variabelen we gebruiken, hij gaat er van uit dat het steeds hex getallen zijn. Staan de getallen in een ander talstelsel, dan worden foutieve uitkomsten bekomen. De processor zal voor zowel de two's complement getallen als voor de BCD voorstelling vlaggen aanpassen (genereren), zodat aangepaste software de nodige correcties kan doorvoeren. Voor de BCD voorstelling is er zelfs een instructie voorzien, die de correctie doorvoert. Let wel, de vlaggen en de correctie instructie werken slechts in welbepaalde gevallen. De instructie kan in geen enkel geval gebruikt worden om een omzetting tussen talstelsels door te voeren. Aangezien je heel uitzonderlijk two's complement getallen zal verwerken met deze core, gaan we daar niet verder op in. Voor de BCD voorstelling gebeurt dit wel meer. De correctie
59 instructie is de Decimal Adjust Accumulator. Ze werkt enkel vlak na de optelling van twee 8 bit getallen, die voor de berekening reeds in de BCD notatie stonden. Deze instructie kan niet gebruikt worden om een hex getal om te zetten naar een bcd notatie. Voorbeeld: mov a,#15H
;zet het getal 15H in de accu
;Je zou kunnen denken dat de instructie zonder de H gebruikt ;moet worden: mov a,#15 ;Stel dat we dit zouden doen, dan zal de assembler het getal ;eerst omrekenen naar het hex talstelsel, waardoor volgende ;instructie uitgevoerd wordt: mov a,#0fH, wat natuurlijk niet ;het gewenste BCD getal 15 = 0001 0101 is. mov R0,#26H add a,R0
;zet het getal 26H in R0 ;maak de optelling
;na de optelling staat er in de accumulator 15H + 26H = 3bH da
a
;voer de correctie uit
;nu staat er in de accumulator 41H wat wel de juiste BCD som is.
2.5 LOGISCHE INSTRUCTIES:
2.5.1 INLEIDING: Bij de wiskundige instructies wordt de inhoud van een register, of een operand als een 8 bit getal gezien. De bits binnen de byte horen bij elkaar, en vormen een geheel. Bij de logische operaties is dit niet het geval. Elke bit wordt als een afzonderlijke variabele gezien, en bij een logische bewerking tussen twee bytes worden de berekeningen uitgevoerd op de overeenkomstige bits binnen de bytes. Er zijn dus 8 resultaten na de bewerking. De accumulator heeft nog altijd een bevoorrechte rol, alhoewel ook andere registers gebruikt kunnen worden voor het uitvoeren van sommige instructies. 2.5.2 ANL/ORL/XRL: Met de ANd Logical instructie worden de overeenkomstige bits van de variabelen ge-and.
60 OR Logical en eXclusive oR Logical werken op een vergelijkbare manier. Voorbeelden: mov a,#10101010b anl
a,#11110000b
;we laden een binair getal ;in a ;voeren de and bewerking ;uit met een constante
na de bewerking zal in de accu 10100000b staan. De and instructie kan gebruikt worden om een deel van een register op nul te zetten, zonder dat de andere bits wijzigen. mov a,#10101010b orl
a,#11110000b
;we laden een binair getal ;in a ;voeren de or bewerking ;uit met een constante
na de bewerking zal in de accu 11111010b staan. De or instructie wordt soms gebruikt om een deel van een register op 1 te zetten, zonder de andere bits aan te passen.
2.5.3 SPECIFIEKE ACCUMULATOR INSTRUCTIES:
CLR A
met deze instructie worden alle bits in de accu op 0 gezet
CPL A
ComPLement de accu maakt een ones complement van de waarde in de accu
Voorbeeld: mov a,#11100000b cpl a ;de accu bevat nu 00011111b, wat het complement is van 1110000 clr
a
;de accu bevat nu 00000000b
61 Rotate instructies schuiven de bits in de accumulator. De tweede letter in de instructie geeft de richting aan waarin de bits opgeschoven worden: Left of Right. Het is ook mogelijk om de carry als negende bit mee door te schuiven. Er wordt dan aan de instructie een C toegevoegd. In het voorbeeld wordt grafisch weergegeven wat de instructie doet. voorbeeld: mov a,10101011b clr c RL A
;zet in de accu 10101011b ;zet de carry vlag op 0 ;A=01010111b CY=0
RR RR
A A
;A=10101011b ;A=11010101b
CY=0 CY=0
RLC RLC
A A
;A=10101010b ;A=01010101b
CY=1 CY=1
RRC A
;A=10101010b
CY=0
62 SWAP A
;Deze instructie verwisselt in de accu de ;hoge nibble en de lage nibble van plaats.
voorbeeld: mov a,#11110000b swap a na de instructie staat er in de accumulator:
a=00001111b
2.6 VERPLAATS INSTRUCTIES: Deze instructies verplaatsen 8 bit getallen in het systeem. Hierbij komen alle registers en het external memory in aanmerking. Het external code memory kan echter niet geschreven worden. Hiertoe ontbreekt het aan instructies, en controle lijnen. De MOV instructies werden reeds als voorbeeld behandeld. We beperken ons hier tot de 3 buitenbeentjes: PUSH, POP, XCH. De eXCHange instructie verwisselt de inhoud van de tweeregisters. De xchd werkt slechts op de laagste 4 bit in de betreffende registers. Een voor beeld is terug te vinden in de uitgebreide instructieset. De PUSH en POP instructies zijn heel belangrijk. Ze worden STACK OPERATIES genoemd. De stack ( ook wel stapelgeheugen) is niet fysiek tastbaar. Hij bestaat uit een aantal registers, die elkaar in adres moeten opvolgen, en waarvan we zowel plaats als aantal zelf kunnen bepalen. Die registers mogen we in ons programma niet gebruiken. Kiezen we er te veel, dan blijven er een deel niet gebruikt. Kiezen we er te weinig, dan zal het systeem vastlopen. Het aantal dat we moeten nemen is afhankelijk van het programma dat we door de controller laten uitvoeren. Sommige controllers hebben een stack van 6 diep, andere gebruiken een minimale stack van 128 bytes. Voor de ADuC8xx volstaan meestal 20H bytes (uit ervaring). Als het programma klaar is moet je, in principe nagaan dat de stack voldoet aan de gewenste afmetingen. Bij de stack hoort ook een STACKPOINTER. Dit SFR register moet geladen worden met het laagste adres van de gereserveerde registers. De SP wordt impliciet gebruikt als adresregister voor indirecte adressering. Telkens er een waarde naar de stack geschreven wordt (PUSH), zal de SP eerst automatisch met 1 verhoogd worden, waardoor hij naar de volgende vrije locatie wijst. Dan wordt de waarde naar het register in de stackzone geschreven. Bij het lezen van een byte uit de stack (POP) gebeurt het
63 omgekeerde (eerst lezen dan SP-1). De stack werkt als een LIFO (Last In/ First Out) memory. Er wordt niet onthouden wat er waar zit, er is enkel de SP die aangeeft waar de laatste variabele op de stack zit.
STACKPOINTER
REGISTERS DIE GERESERVEERD WORDEN VOOR DE STACK. ADRESSERING GEBEURT VIA DE STACKPOINTER OF SP REGISTER
De PUSH instructie wordt gebruikt om de inhoud van een register tijdelijk op te slaan, zonder dat de gebruiker zich zorgen hoeft te maken over waar de info wordt weggeschreven. Het register kan daardoor vrij gebruikt worden. Later kan de inhoud met de POP instructie hersteld worden. Voor de PUSH instructie moet het adres van de direct adresseerbare locatie opgegeven worden, die vrij moet gemaakt worden. Bij de POP instructie met vermeld worden waar de informatie opnieuw geplaatst moet worden. Dit mag een ander of hetzelfde direct adresseerbaar register zijn.
PUSH
POP
Beide instructies gebruiken de SP als adresregister. Push en pop zijn pre_increment en post_decrement indirecte adressering instructies. Indien enkel de PUSH en de POP instructies de stack zouden gebruiken, dan is het eenvoudig om de benodigde ruimte voor de stack te bepalen. Je hoeft maar het maximaal aantal PUSH-instructies te tellen, dat niet door een POP instructie wordt gevolgd. De CPU gebruikt ook de stack, zonder dat de gebruiker expliciet het bevel hiertoe geeft. De CPU zal de PC naar de stack schrijven telkens het programma op een plaats onderbroken wordt, waar later de draad weer opgenomen moet worden (dit is het geval bij de CALL instructies en bij interruptverwerking). Aangezien de PC 16 bit groot is, worden telkens twee bytes gebruikt. Gelukkig kan de programmeur weten wanneer de CPU de stack heeft gebruikt, zodat de eigen informatie niet verloren gaat.
64 Omdat de stack niet meer is dan een gereserveerde geheugenruimte, met bijhorende adrespointer (SP) zijn er heel wat mogelijkheden om het systeem overhoop te halen:
de registers die als stack ruimte dienen, worden elders nog gebruikt er worden meer gegevens op de stack geplaatst dan er registers voorbehouden zijn (stack overflow) er worden meer gegevens van de stack gelezen dan er weggeschreven werden (stack underflow) de gegevens worden in een foutieve volgorde uit de stack gelezen er wordt informatie die door de CPU op de stack werd geschreven veranderd de stackpointer wordt veranderd tussen twee stack operaties
De stackpointer wordt door de CPU na RESET automatisch met de waarde 07H geladen. Omdat dit startadres van de stack in registerbank 2 zit, en we de registerbanken in de meeste toepassingen gebruiken, wordt de SP meestal door een gebruikersprogramma met een andere waarde geladen. Voorbeeld: mov sp,#080h push acc
;de stackpointer wordt met 80h ;geladen ;de inhoud van de accumulator ;wordt naar het adres 81h ;geschreven
let op: in deze instructie wordt niet de letter a gebruikt om de accumulator aan te duiden, maar de letters acc. Heel wat instructies gebruiken de accumulator als speciaal register. Er bestaan ook verkorte opcodes voor instructies die de accumulator gebruiken. Bij de push en pop instructies bestaat er geen verkorte opcode voor de accumulator. Als we een verkorte instructie willen gebruiken, moeten we in de mnemonic de letter a gebruiken. Wensen we het register met het adres van de accumulator (directe adressering), dan worden de letters acc gebruikt. In het labo wordt dit verder verduidelijkt, aan de hand van enkel voorbeelden. push b
;het b register wordt op de stack ;gezet op adres 82h
pop
b
pop
acc
;het b register wordt geladen met ;de informatie van adres 82h ;de sp staat na de instructie op ;81h ;de accumulator wordt geladen met ;de informatie van adres 81h
65 ;de sp staat na de instructie op ;80h merk op dat het adres 80h niet gebruikt werd. Ga na wat het volgende programma doet! mov sp,#030h push acc push b pop pop
acc b
2.7 BIT MANIPULATIES: De ADuC8xx is een microcontroller voor industriële toepassingen. De meeste hiervan sturen bitvariabelen, en lezen die ook binnen. Pompen, ventielen, motoren, lampen,..., kunnen gezien worden als bit variabelen, omdat voor elke uitgang slechts 2 toestanden voorkomen: aan/uit. Einderitschakelaars, niveau detectie, bedieningsknoppen, ... zijn ook bit variabelen. Voor dit type in- en uitgangen, wensen we per variabele maar 1 aansluiting te gebruiken. Een poort bestaat uit 8 bit ( 8 aansluitingen). Een poort wordt gebruikt om 8 variabelen in te lezen/aan te sturen. Het is dan heel handig, als je elke pin van een poort als individuele variabele kan aansturen/lezen. De CPU core laat niet enkel toe dat de belangrijkste SFR's bitsgewijs aangesproken kunnen worden, maar een deel van de gewone registers kunnen op die manier gebruikt worden. Voor de bit instructies werkt de CARRY ALS BIT ACCUMULATOR. Het aantal instructies is beperkt, zoals je kan zien in de samenvatting van de instructieset. De bits, behalve de carry, worden aangesproken met een 8 bit adres. Er zijn dus 256 individueel adresseerbare bits in de CPU registers. 128 bits zijn terug te vinden in de GPR's (General Purpose Registers), de overige 128 zijn voorbehouden voor de SFR's, waarvan het adres eindigt op een 0 of een 8. Niet alle bit adressen in de SFR's zijn geïmplementeerd (aanwezig). In volgende twee figuren zijn de bitadressen van de GPR’s opgenomen, en de lijst van de SFR’s die bitadresseerbaar zijn. In de lijst staan ook de resetwaarden van de registers.
66
67
68 2.8 PROGRAMMA EN MACHINE CONTROLE: Deze instructies laten toe om het programmaverloop te wijzigen, al of niet afhankelijk van de toestand van een (bit)variabele. Hierdoor is het niet alleen mogelijk om het programma een andere weg te laten volgen, afhankelijk van het resultaat van een berekening of variabele, maar ook om bepaalde delen programma vanuit verschillende plaatsen op te roepen. In eerste instantie moeten we duidelijk het verschil aantonen tussen SPRONG OF JUMP instructies en CALL OF "ROEP_OP" instructies. De jump instructies voeren een, al of niet, voorwaardelijke sprong uit naar het adres dat in de instructie is opgenomen. De programmeur wil niet terugkeren naar het adres dat door de sprong verlaten werd, en wil dit RETURN OF TERUGKEERADRES dan ook niet bewaren. Bij een voorwaardelijke sprong zullen we het programma verloop afhankelijk willen maken van de toestand van een variabele. Afhankelijk van het aantal adresplaatsen dat met de spronginstructie overbrugd kunnen worden, spreken we van ABSOLUTE/LONG OF RELATIEVE sprongen.
De long jump (LJMP) gebruikt 16 bit opcode om het adres van de bestemming te bevatten. Daardoor is het sprongbereik 64K.
Bij de absolute sprong (AJMP) wordt slechts 11 bit adres in de opcode geplaatst. De hoogste 5 bit zullen dezelfde zijn als van het adres waar de sprong instructie staat. Door de beperking in het aantal adres bits, kan de sprong alleen uitgevoerd worden binnen eenzelfde blok van 2K.
De relatieve sprong plaatst in de opcode een 8 bit, two's complement offset, ten opzichte van de programcounter met het adres van de instructie die volgt op de relatieve sprong, en de plaats naar waar gesprongen moet worden. Dit heeft voor gevolg, dat er slechts maximaal 127 locaties voorwaarts, of 128 locaties terug gesprongen kan worden. Het sprongbereik is beperkt, maar door de instructies deskundig te kiezen geeft dit meestal geen probleem.
We moeten ook nog een onderscheid maken tussen de conditional en unconditional jumps. Bij de conditional (voorwaardelijke) sprong, is het al of niet springen afhankelijk van de waarde van een (bit/byte)variabele. Bij de niet voorwaardelijke (unconditional) sprong is dit niet het geval, de sprong wordt steeds gemaakt. Als bij een voorwaardelijke sprong, de sprong niet gemaakt wordt, dan voert de CPU de instructie uit die vlak na de sprong instructie staat.
69
Voorbeelden: mov a,#ffh nog: add a,#01h ljmp nog
;in accu ffh laden ;eentje bij optellen ;blijf dit steeds herhalen
her: mov a,#00h nog: add a,#01h jnc nog
;in accu 00h laden ;eentje bij optellen ;het programma wordt nu 256 keer ;doorlopen ;na 256 optellingen wordt alles ;opnieuw opgestart
ljmp her
mov a,#01h nog: djnz a,nog ljmp start
mov a,#00h nog: djnz a,nog ljmp start
mov a,#25h cjne a,#25h,nog
;getal 01h in de accu zetten ;verminder de accu met 1, ;indien niet nul naar nog ;anders volgende instructie ;terug naar label start ;wordt uitgevoerd na 1*djnz
;getal 0h in de accu zetten ;verminder de accu met 1, ;indien niet nul naar nog ;anders volgende instructie ;terug naar label start ;wordt uitgevoerd na 256*djnz
;25h in de accumulator zetten ;als accu niet gelijk aan 25 ;naar nog, anders volgende instructie
De cjne instructie gebruiken we ook op een speciale manier. De controller beschikt niet over een instructie om twee getallen met elkaar te vergelijken. Met de cjnz instructie kan dat wel. Deze instructie past de carry vlag aan, ongeacht of de sprong uitgevoerd wordt of niet:
70
nog:
mov a,#X cjne a,#Y,nog
;zet in de accu het getal X ;vergelijk met het getal Y ;ongeacht het resultaat van de test ;gaat het programma verder op het ;label nog
de carry wordt aangepast volgens het resultaat van de schijnaftrekking aY, hier dus X-Y. is X>Y dan carry op 0 is X=Y dan carry op 0 is X
De software CALL is een instructie die expliciet opgenomen wordt in het programma. Het aangeroepen programma is dan ook een subroutine. Bij de hardware CALL wordt er geen instructie uitgevoerd, maar zal de CPU hardwarematig het programmaverloop aanpassen. Dit gebeurt enkel bij het opstarten van een interrupt routine. Je kan een interruptroutine zien als een subroutine die op een hardwarematige manier opgestart wordt. In elk geval, moet na het uitvoeren van de routine de controle terug overgedragen worden aan het hoofdprogramma. Een hardware CALL noemen we verder in de tekst een INTERRUPT.
Bij alle CALL instructies wordt de stack gebruikt om het terugkeeradres te bewaren. De lengte van de stack wordt dus niet enkel bepaald door het aantal PUSH en POP instructies, maar ook door het aantal GENESTE CALLS EN HET AANTAL INTERRUPTS.
71 Een subroutine of een interruptroutine moeten afgesloten worden met een instructie die het return adres van de stack haalt, en daar naar toe springt. Hiervoor worden de RETURN of RET instructies gebruikt. De RET instructie staat op het einde van een gewone subroutine, de RETI (RETurn from Interrupt) instructie wordt gebruikt op het einde van een interruptroutine. Interruptroutines worden door hardware opgestart, en het is dan ook onmogelijk om ten opzichte van het hoofdprogramma te weten wanneer ze uitgevoerd gaan worden. Je weet niet welke registers op dat ogenblik in gebruik zijn, en door de interruptroutine gebruikt mogen worden. Gelukkig beschikken we over de PUSH en POP instructies, om tijdelijk registers vrij te maken. Omdat de meeste bewerkingen op alle registers uitgevoerd kunnen worden, en omdat er een massa registers beschikbaar zijn, is het meestal slechts nodig om de accu, PSW en eventueel de DPTR op de stack te bewaren. Hierdoor kan het aantal bytes stack beperkt blijven. Dit is een van de grote verschillen tussen een gewone CPU en een controller core. Bij een gewone CPU zullen er minder registers zijn waardoor de stack heel groot moet kunnen worden (8086 = 64K), en vlot gebruikt kan worden. Helaas gebruiken hogere programmeertalen de stack om parameters door te geven tussen subroutine en hoofdprogramma. Dit is bij een controller uiterst moeilijk, omdat de stack een beperkte grootte heeft, en de access naar het extern geheugen moeilijk verloopt (stack is steeds inwendig in de controller). De NOP instructie wordt door de CPU uit het geheugen gelezen, zodat ze een bepaalde tijd duurt. Bij de uitvoering van de instructie gebeurt er niets. De instructie lijkt dan ook overbodig. Er zijn echter bepaalde situaties, waar het handig is om over een instructie te beschikken die alleen CPU tijd in beslag neemt (opwekken van een korte puls, gebruik van de MDU, vertragingsroutines,...).
2.9 DRIVERS, LED’S EN SCHAKELAARS: Een programma heeft maar zin als er gegevens met de omgeving uitgewisseld kunnen worden. In de labo_opstelling zijn er 6 schakelaars op poort 3, 2 schakelaars op poort0, 8 LED’s op poort 2, een potentiometer op adc7 en een LCD scherm via poort 0. Hiermee beschikt het systeem over minimale I/O. Het aansturen van de LED’s gebeurt via de MOV P2,xxx instructies. Elke uitgangsbit stuurt 1 LED aan. Het inlezen van de schakelaars gebeurt met de MOV xxx,P0 (dipswitchen) Of MOV xxx,P3 (4 functietoetsen) instructies. Hiermee wordt een binair (hex) getal van 8 bit ingelezen. In het geval van P3 zijn niet alle bits door
72 de schakelaars bepaald. Door de ANL of ORL instructies te gebruiken kunnen de niet gewenste bits op 1 of 0 gezet worden. Je kan ook gebruik maken van de bitinstructies om de schakelaars af te vragen. In de aducez2.inc file zitten tal van nuttige functies. Meer uitleg over het gebruik hiervan kan je in de include file vinden. Deze functies worden ook wel drivers genoemd, omdat ze het aansturen van hardware voor hun rekening nemen. We beperken ons hier tot een summiere beschrijving en een bondige uitleg van de belangrijkste routines die de seriële poort gebruiken. Dit is alleen mogelijk, als er op de PC een programma loopt dat een terminal emulatie uitvoert (hyperterminal, Tera Term). Alle communicatie tussen de controller en de PC verloopt dan via de seriële poort. Gegevens die naar de PC gestuurd worden, verschijnen op het scherm, toetsaanslagen op het klavier worden via dezelfde seriële poort naar de controller doorgegeven. Door de terminal emulatie gebeurt alle communicatie met ASCII codes. Met elke code komt een symbool overeen. In de tabel op de volgende bladzijde zijn de belangrijkste ASCII codes weergegeven.
INITSIO:
zal de poort instellen op 9600 bits per seconde. De routine moet uitgevoerd worden nadat de pll ingesteld is. SIOOUTCHAR: stuurt een ASCII code in de accu naar het scherm. SIOOUTBYTE: zet de inhoud van de accu om in een ASCII code per nibble, een stuurt die vervolgens naar het scherm. SIOOUTMSGA: in de dptr moet het start adres van een ASCII tabel, in code memory, staan. De tabel moet afgesloten worden met een null karakter. De volledige string wordt naar het scherm gestuurd. SIODISPDPTR: de inhoud van de dptr wordt als 4 hex karakters op het scherm afgedrukt. SIOINCHAR: leest een ASCII code in in de accumulator. SIOINBYTE: leest 2 ASCII codes binnen, en vormt ze om tot een binair getal in de accu. SIOINBUFA: leest een string ASCII codes binnen. De string moet afgesloten worden met een cr, en mag maximaal 32 karakters groot zijn. De karakters komen vanaf strtbuf t.e.m. endbuf in de GPR’s te staan. ASCBINTRANS: vormt een ASCII code in de accumulator om in de bijhorende binaire nibble. Enkel toepasbaar voor ASCII codes van 0-9 en A-F. BATRANS: vormt een binaire nibble in de accu om naar de bijpassende ASCII code. ASCII1: deze drie routines worden gebruikt voor het omvormen ASCII2: van 1, 2 of vier ASCII codes in een binair getal.
73
ASCII4:
R0 werkt als pointer, de accu of de dptr wordt als output gebruikt. XONXOFF: flow control bij seriële communicatie.
Let op!!! Sommige routines gebruiken CPU registers, zodat de inhoud ervan na de subroutine niet meer bruikbaar is. In de monitor listing kan je meer specifieke info vinden in de commentaar voor elke subroutine. RIGHT 0000 LEFT 0
0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1 2 3 4 5 6 7 8 9 A B C D E F
0000 0
NUL
SOH
STX
ETX
EOT
ENQ
ACK
BEL
BS
HT
LF
VT
FF
CR
SO
SI
0001 1
DLE
DC1
DC2
DC3
DC4
NAK
SYN
ETB
CAN
EM
SUB
ESC
FS
GS
RS
US
0010 2
SB
!
“
#
$
%
&
‘
(
)
*
+
,
-
.
/
0011 3
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
0100 4
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
0101 5
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
0110 6
‘
a
b
c
d
E
f
g
h
i
j
k
L
m
n
O
0111 7
p
q
r
s
t
U
v
w
x
y
z
{
|
}
~
DEL
ASCII CODE TABEL Er zijn ook functies beschikbaar voor het aansturen van de LCD module op de kaart. De routines hebben dezelfde naam als die van de seriële poort, maar worden voorafgegaan door LCD i.p.v. SIO. Input via het LCD scherm is niet mogelijk. LET OP! De uitwerking van de ascii codes onder 20h en boven 7fh is anders dan bij de sio routines. Raadpleeg de driver voor meer uitleg. Mogelijk zitten er in de include file nog handige routines.
2.10 TIPS EN TRUCS:
2.10.1 INLEIDING: In dit deel van de cursus willen we enkel tips en trucs aanbrengen. Het zijn veel gebruikte technieken voor standaard problemen. De oefeningen in het labo maken hier gebruik van.
74
2.10.2 SOFTWARE TIJDSVERTRAGING: Een microcontroller voert relatief snel instructies uit. De controller uit het labo loopt op 2.097152MHz (kan via het pllcon sfr ingesteld worden tussen 0.13 en 16.78 MHz). De CPU heeft 12 klokpulsen nodig om een elementaire operatie uit te voeren. Een elementaire operatie is het verplaatsen van 1 byte data tussen het geheugen en de CPU. De CPU voert de ene elementaire operatie na de andere uit (een elementaire operatie wordt een CYCLE of cyclus genoemd). Er zijn er nodig om een instructie te lezen en uit te voeren. Sommige instructies kunnen in een cycle gelezen en uitgevoerd worden, andere in 2 of 4. ( Sommige nieuwe 8051 controllers zijn sneller omdat ze per cycle minder klokpulsen nodig hebben en/of ze kunnen met een hogere klokfrequentie werken.) Volgend programma laat een looplicht zien op de LED’s van poort 2. org lus:
0000h
mov a,#00000001b rl a mov p2,a ljmp lus
;start adres programma ; 1 getal dat naar P2 gaat ; 1 we schuiven het getal op ;zodat een looplicht bekomen ;wordt ; 1 laten zien ; 2 opnieuw schuiven, laten zien ;enz.
Hoeveel tijd is er nodig om de lus een keer uit te voeren? In de instructie lijst staat het aantal cycli dat elke instructie nodig heeft. In bovenstaand programma zijn ze in vet opgenomen in de commentaar. Voor de instructies in de lus zijn er 1+1+2=4 cycli nodig. Het programma loopt 42000 keer per seconde (2MHz/12klokpulsen per cyclus=175000 cycli per seconde of 6uS per cyclus). Het lijkt alsof alle LED’s tegelijk licht geven. Om het programma te vertragen gebruiken we de djnz instructie. Hiermee kunnen we op een compacte en eenvoudige manier veel instructies uitvoeren. Het volgende programma duurt 511 uS. Loop:
mov r0,#ffh djnz r0,loop
; 1 register laden met 255 ; 2 verminder r0 met 1 en indien ;niet nul spring naar loop anders ;verder gaan
De djnz instructie duurt 12uS en wordt 255 keer doorlopen zodat er
75 3060uS nodig zijn. De mov instructie duur 6uS. De totale looptijd is dan 3066uS. Na de uitvoering is het register r0=000h. Stel dat we volgend programma uitvoeren, hoe lang zal dit duren? loop:
mov r0,#00h djnz r0,loop
Als r0=00h hoeveel is dan r0-1? 00h-1=ffh. De lus wordt dan ook 256 keer doorlopen. Wensen we de vertragingstijd verder op te voeren kunnen we meerdere lussen door elkaar gebruiken. Volgend voorbeeld geeft een vertraging van ongeveer 1 seconde:
loop:
mov mov djnz djnz
r1,#00h r0,#00h r0,loop r1,loop
;aantal keer buitenste lus ;aantal keer binnenste lus ;binnenste lus ;buitenste lus
De duurtijd van dit programma is: 6+6+(((12*256)+12)*256)=789516uS. Merk op dat het niet zo eenvoudig is om er voor te zorgen dat het exact 1000000uS tijdsvertraging geeft. We zullen software vertragingen dan ook maar gebruiken om “ongeveer” (10% juist) tijden af te passen. Om de tijdsvertraging afhankelijk te maken van de schakelaars op het bord kunnen we bvb. het volgende programma gebruiken: loop1: loop:
mov r1,#00h mov r0,p0 djnz r0,loop djnz r1,loop1
;aantal keer buitenste lus ;aantal keer binnenste lus ;p0=schakelaars ;binnenste lus ;buitenste lus (r0 moet herladen ;worden want is nu 000h)
De looptijd wordt gegeven door volgende formule: 6+((6+(12*p3)+12)*256)= 13830uS als p0=03h =787974uS als p0=ffh De programmeur kan een oneindig aantal variaties op bovenstaande programma’s bedenken. Je moet wel narekenen wat de uiteindelijke vertraging zal zijn.
76 2.10.3 AANMAKEN EN UITLEZEN VAN TABELLEN: Er zijn veel toepassingen waar tabellen gebruikt worden. Volgend programma laat toe om een willekeurig looplicht te laten zien op de LED’s. ;declaratie startadres start equ
0000h
;startadres programma
;gebruikte del1 del2 del3
20h 21h 22h
;gebruikt voor tijdsvertraging ;idem ;idem
loop:
delay: delay1: delay2:
tabel:
registers equ equ equ org
start
mov mov movx mov lcall inc djnz ljmp
dptr,#tabel r7,#04h a,@dptr p2,a delay dptr r7,loop start
;startadres tabel in dptr ;aantal bytes in de tabel ;waarde uit tabel halen ;laten zien ;tijdsvertraging oproepen ; adres tabel in dptr+1 ;herhaal tot r4=000h
mov mov mov djnz djnz djnz ret
del3,#02h del2,#00h del1,p0 del1,delay2 del2,delay1 del3,delay1
;laden registers ;tijdsvertraging ;tijdsvertraging instelbaar
db db db db
10000001b 01000010b 00100100b 00011000b
;eerste waarde in tabel ;tweede op een adres hoger ;nog een adres hoger ;vierde byte
;einde subroutine
Tabel is een label dat als waarde het adres krijgt van de geheugenlocatie waar de byte 10000001b staat. Het toekennen van die waarde gebeurt door de assembler die het programma omzet. Het db directief staat voor DEFINE BYTE. Hiermee wordt een constante mee opgenomen in het programma. De volgende variant van het programma gebruikt geen teller om het einde van de tabel te detecteren, maar een verboden code. Dat is een constante die geen deel uitmaakt van de eigenlijke data in de tabel, maar
77 aangeeft dat het de laatste entry van de tabel is. ;declaratie startadres start equ
0000h
;startadres programma
;gebruikte del1 del2 del3
20h 21h 22h
;gebruikt voor tijdsvertraging ;idem ;idem
loop: loop1:
delay: delay1: delay2:
tabel:
registers equ equ equ org
start
mov movx cjne ljmp mov lcall inc
dptr,#tabel a,@dptr a,#ffh,loop1 start p2,a delay dptr
ljmp
loop
mov mov mov djnz djnz djnz ret
del3,#02h del2,#00h del1,p0 del1,delay2 del2,delay1 del3,delay1
;laden registers ;tijdsvertraging ;tijdsvertraging instelbaar
db db db db db
10000001b 01000010b 00100100b 00011000b 11111111b
;eerste waarde in tabel ;tweede op een adres hoger ;nog een adres hoger ;vierde byte ;geeft aan einde tabel
;startadres tabel in dptr ;waarde uit tabel halen ;niet 0ffh in accu dan loop1 ;anders opnieuw starten ;laten zien ;tijdsvertraging oproepen ;waarde=adres tabel in dptr ;+1 ;volgende lezen
;einde subroutine
In dit programma kunnen we de tabel verlengen zonder de code aan te passen. Let op!! De waarde ffh mag alleen op het einde van de tabel voorkomen.
Een tabel kan ook gebruikt worden om aan tekst boodschap in het geheugen op te slaan. Volgend programma geeft hier een voorbeeld van (op de PC loopt het programma hyperterminal):
78 start
loop: loop1:
equ
0000h
org
start
mov lcall lcall mov lcall ljmp
dptr,#msg1 outmsga delay dptr,#msg2 outmsga loop1
;startadres programma
;adres eerste boodschap ;naar scherm PC ;wachten ;adres tweede boodschap ;naar scherm PC ;stoppen controller
; delay is een subroutine die ongeveer 1.5 seconde duurt. ; de routine gebruikt r0,r1 en r2. delay: delay1:
msg1: msg2:
mov mov mov djnz djnz djnz ret
r2,#02h r1,#00h r0,#00h r0,delay1 r1,delay1 r2,delay1
db ff,”mij kan je bovenaan links lezen”,000h db cr,lf,lf,beep,”ik beep 2 keer en sta 2 lijnen lager” db “en neem 2 lijnen in beslag” db cr,lf,”je moet ook 1 seconde op mij wachten”,beep,000h
Siooutmsga is een monitorroutine die een ASCII string afdrukt op het scherm van de PC. Het startadres van de string moet in de dptr staan. Het laatste karakter van de string moet 00h zijn. Wat tussen ‘’ staat wordt door de assembler omgezet in zijn ascii codes. Op het scherm van de PC is het volgende te zien:
79 mij kan je bovenaan links lezen ik beep2 keer en sta 2 lijnen lager en neem 2 lijnen in beslag je moet ook 1 seconde op mij wachten
Om variabelen af te drukken zijn er andere monitorroutines beschikbaar.
2.10.4 EEN TABEL GEBRUIKEN OM TE VERTALEN: In bepaalde gevallen is er geen wiskundig verband tussen twee getallen. Het ene kan dus niet via een berekening omgezet worden naar het andere. Een voorbeeld hiervan is het verband tussen de temperatuur en de spanning van een thermokoppel. De eenvoudigste manier om de omzetting te doen is via een tabel. In die tabel steken we voor elke mogelijke spanning van het thermokoppel de overeenkomstige temperatuur. Een ander voorbeeld is het omzetten van een binair getal naar een uitlezing op een 7-segment uitlezing. De hardware die we gebruiken is fictief. Poort 2 wordt gebruikt voor het aansturen van een 7-segment display. P2.0=a P2.1=b P2.2=c P2.3=d P2.4=e P2.5=f P2.6=g P2.7=dp
a f
g
e
b
c d
dp
80 Poort 0 wordt gebruikt voor het inlezen van 4 schakelaars. De waarde van de schakelaars wordt omgezet naar een uitlezing op de 7-segment display.
loop:
; ; ; ;
org
0000h
;startadres programma
mov mov anl swap
dptr,#tabel a,p0 a,#f0h a
movc mov ljmp
a,@a+dptr p2,a loop
;startadres tabel laden ;lees de schakelaars ;laagste 4 bits op 0 ;4 bovenste bits omruilen ;met 4 onderste ;vertalen via tabel ;naar 7 segment uitlezing ;herhaal
hier staan de binaire patronen die nodig zijn om de cijfers 0-9 en de letters a-f weer te geven op het 7-segment display. Let op!! Het display wordt actief laag aangestuurd: een 0 doet de LED licht geven, een 1 schakelt de LED uit.
tabel:
db db db db db db db db db db db db db db db db
11000000b 11111001b 10100100b 10110000b 10011001b 10010010b 10000010b 11111000b 10000000b 10010000b 10001000b 10000011b 11000110b 10100001b 10000110b 10001110b
;binair patroon voor 0 ;1 ;2 ;3 ;4 ;5 ;6 ;7 ;8 ;9 ;A ;b ;C ;d ;E ;f
Merk op dat er geen wiskundig verband bestaat tussen de “display code” voor het 7-segment display en het binaire getal dat via poort 0 ingelezen wordt. De dptr wijst steeds naar het adres van de eerste entry in de tabel. De waarde in de accu, afkomstig van p0, vormt een offsetwaarde van 00h-00fh, afhankelijk van de 4 bovenste schakelaars. De movc a,@a+dptr instructie leest naar de accu wat er op het adres staat gevormd door de som van dptr en a. Na de instructie zal de dptr niet van waarde veranderd zijn, de accu werd overschreven met het getal dat uit het geheugen gelezen is. Deze instructie werkt enkel via het “code memory”.
81
2.10.5 HEX, BCD, BIN INLEZEN EN UITSCHRIJVEN: Alle communicatie tussen de PC en de controller gebeurt in ASCII code. Voor elk karakter dat op het scherm komt, of via het klavier verzonden wordt, wordt een 8 bit code gebruikt. Het symbool dat met die code overeenkomt kan je opzoeken in de ASCII code tabel in paragraaf 2.9. Voor de verdere bespreking gebruiken we de driver routines inchar en outchar. Sioinchar wacht op een ASCII code. Komt die toe op de seriële poort, dan wordt ze in de accu geplaatst en wordt de routine afgesloten. Het getal kan binair ingegeven worden. We krijgen per bit een 8-bit ASCII code doorgegeven: 30h voor 0 en 31h voor 1. Gebruiken we een verkorte notatie dan zijn de mogelijke ASCII codes 30h t.e.m. 39h voor de cijfers 0 t.e.m. 9 en 41h t.e.m. 46h voor de letters A t.e.m. F of 61h t.e.m. 66h voor de letters a t.e.m. f. Siooutchar zal voor dezelfde ASCII codes bijhorende symbolen op het scherm plaatsen. Moet een getal binair op het scherm afgedrukt worden, dan zullen we zoveel ASCII codes naar het scherm moeten sturen als er bits in het getal zitten. Bij het decimaal uitschrijven mogen we enkel de ASCII codes voor de cijfers gebruiken. Per symbool geven we eigenlijk 4 bits weer. Bij de weergave van hex getallen mogen ook de letters a-f gebruikt worden. Ook hier staat elk symbool voor 4 bits. Bij het weergeven van een getal zal het programma moeten nagaan wat de waarde van elke individuele bit is (of groepje van 4 bits) om de passende ASCII code te bepalen alvorens die naar het scherm te sturen. Bij het inlezen gebeurt het omgekeerde. Bij het binair inlezen krijgen we 1 ASCII code per bit, bij compacte weergave 1 ASCII code per 4 bits. Vb: Ingave via klavier:10010011 geeft volgende reeks ascii codes: 31h,30h,30h,31h,30h,30h,31h,31h. Per ascii code kan maar 1 bit gebruikt worden bij de wedersamenstelling van het getal. Ingave via klavier:a7 geeft volgende twee ascii codes:61h en 37h. Elke ASCII code is nu 4 bits waard. Volgend programma drukt de inhoud van p0 binair en hex af op het scherm van de PC org
0000h
;start adres programma
mov
a,#ff
;eerst scherm leeg maken
82 loop:
lcall mov lcall mov lcall mov lcall mov lcall ljmp
siooutchar a,p0 schrbin a,#blank outchar a,p0 schrhex a,#cr siooutchar loop
;naar scherm sturen ;lees poort 0 ;binair op scherm zetten ;blank tussen de twee getallen ;poort 0 inlezen ;hex uitschrijven ;cursor vooraan lijn ;naar scherm schrijven ;lus sluiten
; schrbin is een subroutine die de inhoud van de accu op het ; scherm weergeeft als een binair getal. De routine gebruikt de ; accu, psw en r7. schrbin: schrbin1:
mov r7,#08h ;loopcounter (lusteller) rlc a ;bit naar carry schuiven push acc ;accu effe bewaren mov a,#30h ;ascii code 0 in accu addc a,#00h ;a=30h+00h+cy ; de accu zal 030h bevatten als carry=0 en 31h als carry=1 ; 30h is ascii code van 0 en 31h is de ASCII code van 1 lcall siooutchar ;naar scherm PC verzenden pop acc ;accu herstellen djnz r7,schrbin1 ;herhaal tot einde 8 bits ret ; schrhex zal de inhoud van de accu in hexadecimale vorm op het ; scherm plaatsen. De routine gebruikt de accu en psw. Schrhex:
push anl swap cjne schrhex1: jc add schrhex2: add
acc ;effe bewaren op de stack a,#f0h ;hoogste 4 bits afzonderen a ;bits onderaan plaatsen a,#0ah,schrhex1 ;acc<00ah? schrhex2 ;ja, dan springen a,#07h ;neen, dan 007h bij optellen a,#30h ;altijd 030h bij optellen
; wil je weten hoe dit werkt? Neem stukje papier en reken na ; voor alle mogelijke getallen in de accu. lcall pop anl cjne schrhex1: jc add
siooutchar ;naar het scherm sturen acc ;laagste 4 bits verwerken a,#0fh ;hoogste 4 bits afzonderen a,#0ah,schrhex1 ;acc<00ah? schrhex2 ;ja, dan springen a,#07h ;neen, dan 007h bij optellen
83 schrhex2: add
a,#30h
;altijd 030h bij optellen
; wil je weten hoe dit werkt? Neem stukje papier en reken na ; voor alle mogelijke getallen in de accu. lcall ret
siooutchar ;naar het scherm sturen
Onderstaand programma leest een getal binair in via het klavier van de PC en laat het zien op poort 2. Bij de laatste ingave kan op de volgende lijn een nieuw getal ingegeven worden. start
loop:
; ; ; ;
equ
0000h
;start adres programma
org
start
mov lcall
a,#ff outchar
;scherm leeg maken ;weg er mee
lcall mov mov lcall mov lcall ljmp
inbin p2,a a,#lf siooutchar a,#cr siooutchar loop
;binair inlezen byte ;naar LED’s sturen ;volgende lijn nemen op scherm ;cursor moet vooraan de lijn
inbin leest een byte binnen als een reeks van 8 binaire getallen. De routine stuurt een echo naar het scherm. Het ingelezen getal wordt via de accu doorgegeven. De routine gebruikt geen registers.
Inbin:
push b ;wordt gebruikt als lusteller push psw ;de vlaggen gaan anders verloren mov b,#08h ;iets gaan we 8 keer doen inbin1: push acc ;gaan we gebruiken als werkreg. lcall sioinchar ;wachten op eerste ingave lcall siooutchar ;echo naar het scherm van de Pc rrc a ;laagste bit afzonderen ;030h is ASCII code van een 0 en 31h is ASCII code van 1 pop acc ;werk accu laden rlc a ;afgezonderde bit inschuiven djnz b,inbin1 ;herhaal 8 keer pop psw ;vlaggen herstellen pop b ;b herstellen ret
84 2.10.6 OMREKENING HEX NAAR BCD: Bij de omzetting van een hex getal naar een bcd getal gaan we kijken hoeveel keer 10, 100, 1000, … in het getal gaan. Dit doen we door een deling uit te voeren. Bij de eerste deling delen we het getal door 10d (0ah). Het quotiënt van die deling = aantal keer dat 10 in het getal gaat. De rest is de overschot (wat kleiner is dan 10) en dus de eenheden. Het quotiënt kunnen we opnieuw delen door 10d (0ah). Het nieuwe quotiënt geeft aan hoeveel honderdtallen er in het getal zitten. De nieuwe rest geeft het aantal tientallen. We delen evenveel keer als er decimale karakters nodig zijn om het getal weer te geven. Stel het hex getal is 0fch. Een deling door 0ah geeft als quotiënt 19h. De rest is 02h. Er zitten 19h (25d) tientallen in het getal en 2 eenheden. De rest van de deling zit altijd tussen 00h en 09h. Het vorige quotiënt delen we opnieuw door 0ah. 19h/0ah geeft als nieuw quotiënt 02h en als rest 05h. Er zitten 2 honderdtallen in het getal en 5 tientallen. Bij de omzetting van een 8 bit hex getal kan het grootste decimale resultaat 255d zijn. In principe delen we drie keer. Eigenlijk is dat niet nodig omdat het quotiënt van de tweede deling nooit groter kan zijn dan 2, zodat verder delen niet zinvol is. Omdat we in een programma lussen zullen gebruiken voor de omzetting is het meestal eenvoudiger om nog eens te delen. 02h/0ah geeft als quotiënt 00h en als rest 02h. dit zijn dan de honderdtallen. In volgend programma voeren we de omzetting door voor een getal dat we inlezen van de schakelaars. Het decimale resultaat wordt naar het scherm van de PC gestuurd.
loop2:
org
0000h
mov lcall
a,#ff siooutchar
;scherm eenmalig leeg maken ;naar scherm sturen
mov
a,p0
;lees de schakelaars in accu
mov r0,#03h ;max uitkomst is 255 dus 3 ; keer delen (getal bestaat uit drie karakters) loop:
mov div push djnz
b,#0ah ab b r0,loop
;we gaan delen door 10d ;a/b en a=quotient b=rest ;rest bewaren op de stack ;3 keer uitvoeren
85 ; ; ; ; ; ;
op de stack zitten de drie resten. De stack werkt als een LIFO (last in first out). De eerste rest was de eenheden, de laatste de honderdtallen. Bij het poppen komen eerst de honderdtallen dan de tientallen, en als laatste de eenheden. Die waarden moeten nog omgezet worden naar ASCII codes. de ascii codes van 0-9 zijn 30h-39h.
loop1:
mov pop add lcall djnz
r0,#03h acc a,#30h siooutchar r0,loop1
mov
a,#cr
lcall ljmp
siooutchar loop2
;ook in lus zetten ;waarde van stack halen ;omzetting naar ASCII ;naar het scherm sturen ;er zitten drie waarden op ;de stack ;cursor vooraan de lijn ;plaatsen ;naar scherm ;volgende getal omzetten
Voor de omzetting van grotere getallen (groter dan 8 bit) kunnen we de div instructie niet meer gebruiken. We zullen dan een deling moeten gebruiken die getallen van grotere omvang kan verwerken (zie uitleg vermenigvuldigen en delen van grote getallen in paragraaf 2.10.8). Onderstaand programma kan ook gebruikt worden maar duurt heeeel lang. Het staat hier enkel als voorbeeldprogramma. Het is geschreven voor een 16 bit omzetting (maximale waarde 65536 (5 karakters) maar kan aangepast worden voor getallen van onbeperkte afmetingen). Het hex getal wordt ingelezen van het klavier van de PC, de uitkomst komt op het scherm van de PC. ; ; ; ; ; ;
dit programma leest van het scherm een 16 bit hex getal. Het wordt omgezet naar een 24 bit bcd getal. De gebruikte methode gaat van het hex getal 1 aftrekken tot 0 bereikt wordt. Gelijktijdig wordt bij het bcd getal 1 opgeteld. Deze methode trekt op niks en vraagt veel processor tijd. De oefening is enkel ter illustratie.
start
equ
0000h
;start adres programma
hexhoog hexlaag bcdhoog bcdmid bcdlaag
equ equ equ equ equ
20h 21h 22h 23h 24h
;hoogste 8 bits hex getal ;laagste bits hex getal ;hoogste bits bcd getal ;middelste bits bcd getal ;laagste bist bcd getal
org
start
86
lus: scherm
mov lcall mov
a,#ff siooutchar dptr,#msg1
lcall mov lcall
sioinbufa r0,#startbuf ascii2
jc mov mov lcall jc mov
;scherm eenmalig leeg maken ;boodschappeke
op
het
;lees in ;pointer naar start tabel ;2 eerste ASCII codes naar ;hex getal omzetten start ;fout in omzetting hexhoog,a ;opslaan r0,#(startbuf+2) ;volgende 2 ASCII codes ascii2 ;omzetten start ;fout in omzetting hexlaag,a
; er is een geldig hex getal ingegeven. We gaan nu de omzetting ;starten mov mov mov
bcdhoog,#00h bcdmid,#00h bcdlaag,#00h
;initialisatie registers ;idem ;idem
; bij de omzetting gebruiken we geen lusteller maar gaan we ; omzetten tot het hex getal 0 is. Dit kan bij de ingave reeds ; het geval zijn! test:
mov orl jz
a,hexhoog a,hexlaag uit
;nagaan hexhoog en hexlaag=0 ;a=0 als hexhoog=hexlaag=0 ;dan uitkomst naar scherm
; we gaan het 16 bit hex getal met 1 verminderen mov clr subb mov mov subb mov
a,hexlaag c a,#01h hexlaag,a a,hexhoog a,#00h hexhoog,a
;gebruiken een 16 bit ;carry uit verleden weg ;-1 ;terug wegschrijven ;eventuele carry verrekenen ;terug wegschrijven
; nu het bcd getal met 1 verhogen. mov add da mov mov addc
a,bcdlaag a,#01h a bcdlaag,a a,bcdmid a,#00h
;met lage deel starten ;let op dit is een bcd + ;dus corrigeren ;terug wegschrijven ;eventuele carry doorgeven
87 da mov mov addc da mov
a bcdmid,a a,bcdhoog a,#00h a bcdhoog,a
;ook dit is bcd + ;hoogste deel ;terug wegschrijven
; nagaan of we mogen stoppen. Dit is het geval als het hex ; getal=0
uit:
msg1:
ljmp
test
mov lcall mov lcall mov lcall mov lcall mov lcall ljmp
a,bcdhoog siooutbyte a,bcdmid siooutbyte a,bcdlaag siooutbyte a,#lf siooutchar a,#cr siooutchar lus db
;eerst hoge weg ;dan de middelste ;dan de laagste ;cursor lijn lager ;cursor vooraan lijn
“geef een 16 bit hex getal in:”,000h
2.10.7 OMREKENING BCD NAAR HEX: Bij de omrekening van een bcd getal naar een hex getal is er een klein probleem. De controller kan op een bcd getal enkel de add of addc bewerking uitvoeren, om met behulp van de da a instructie, een correct resultaat te produceren. We kunnen wel berekeningen uitvoeren op de individuele nibbles van het getal. Die hebben immers altijd een correcte hex waarde (0bcd=0hex en 009bcd=009hex). Als we een bcd getal van rechts naar links bekijken, stellen we vast dat de waarde van de volgende nibble 00ah groter is (zie decimale getallen). Als we die berekening in een programma plaatsen en de getallen optellen bekomen we de hex waarde. Volgend programma voert dit uit voor een 2 karakter bcd getal. Voor grotere getallen zal je de routines voor het vermenigvuldigen van grote getallen moeten gebruiken. Andere mogelijkheden geven omslachtige programma’s. ; dit programma leest een 2 karakter bcd getal in via het ; klavier van de PC en zet het om naar een 8 bit hex getal. ; de ingave is heel summier. De echo komt pas na het ingeven
88 ; van de 2 karakters van het getal. Na een spatie wordt de ; uitkomst afgedrukt. start
equ
0000h
;start adres programma
uitkomst
equ
20h
;hier uitkomst in stoppen
org
start
mov lcall mov
a,#ff siooutchar dptr,#msg1
lus: scherm
;scherm eenmalig leeg maken ;weg er mee ;boodschap naar het
lcall siooutmsga lcall sioinbyte ;inlezen van een 8 bit getal ; er is geen echo naar het scherm, we doen ook geen foutcontrole ; in de accu zit het bcd getal push lcall mov lcall pop push
acc siooutbyte a,#blank siooutchar acc acc
;effe copie naar stack ;byte naar scherm ;spatie naar het scherm
anl
a,#0fh
;laagste 4 bits afzonderen
;accu herstellen ;nieuwe copie op stack
; die laagste bits moeten niet omgerekend worden mov pop anl swap
uitkomst,a acc a,#f0h a
;bewaren ;opnieuw herstellen accu ;hoogste 4 bits afzonderen ;moeten in laagste 4 bits ;zitten ;deze nibble is 10 keer
mov b,#0ah ; meer waard dan de vorige. mul ab ;vermenigvuldigen add a,uitkomst ;vorige waarde bijtellen ; de maximale uitkomst kan 99d of 63h zijn zodat er maar 1 byte ; nodig is om de uitkomst op te slaan. lcall ljmp
msg1:
db
siooutbyte lus
;uitschrijven naar scherm
cr,lf,”geef getal in: (geen echo naar het scherm)”,000h
89 Hier laten we zien hoe een 4 karakter bcd getal omgezet wordt: bcd getal =1234h (0001 0010 0011 0100 b) Omzetten naar nibbles: 01h 02h 03h 04h (elke nibble zit in een byte) Elke nibble vermenigvuldigen met de passende decimale waarde: 01*1000d 02*100d 03*10d 04*1d Omdat we niet met decimale getallen kunnen vermenigvuldigen wordt dat: 01h*03e8h=03e8h 02h*0064h=00c8h 03h*000ah=001eh 04h*0001h=0004h De som van de producten is 04d2h Merk op dat bij het schrijven van het programma het best een lus kan gebruikt worden. De lus wordt evenveel keer doorlopen als er bcd getallen zijn.
2.10.8 VERMENIGVULDIGEN EN DELEN VAN GROTE GETALLEN: In dit deel van de cursus staan 2 voorbeeld programma’s die voor 32 bit getallen geschreven zijn. Het programma kan aangepast worden naar zo veel bits als nodig. De gebruikte rekentechnieken komen overeen met die voor decimale getallen, maar dan toegepast op binaire getallen. Voor de deling wordt dat de staartdeling en bij het vermenigvuldigen gebruiken we de sommatie van deelproducten. De techniek kan ook op andere niet 8051 controllers toegepast worden. ; ; ; ; ; ;
dit programma zal twee getallen van 32 bit met elkaar vermenigvuldigen. We gaan er van uit dat ze reeds in de registers van de controller staan. De uitkomst wordt ook naar registers geschreven. We gebruiken registers in het indirect adresseerbare gebied. Omdat het een onderdeel is van een groter programma zijn de gebruikelijke org en # directieven
90 ; weggelaten. get1hh get1h get1l get1ll
equ equ equ equ
80h 81h 82h 83h
;adres hoogste byte get1 ;lagere byte ;volgende lagere ;laagste
werkhh werkh werkl werkll
equ equ equ equ
84h 85h 86h 87h
;4 werkregisters
get2hh get2h get2l get2ll
equ equ equ equ
88h 89h 8ah 8bh
;zie hierboven maar get2 ;idem
uithhhh uithhh uithh uith uitl uitll uitlll uitllll
equ equ equ equ equ equ equ equ
8ch 8dh 8eh 8fh 90h 91h 92h 8fh
;64 bit uitkomst
teller telbits
equ equ
20h 21h
;lusteller ;bitteller
; we gaan eerst de uitkomstregisters op 0 zetten
lus:
mov mov mov mov inc djnz
a,#00h ;gaan uitkomst op 0 zetten teller,#08h ;er zijn 8 registers r0,#uithhhh ;r0 als pointer naar uithhhh @r0,a ;naar geheugen schrijven r0 ;pointer aanpassen teller,lus ;herhaal 8 keer
; we gaan eerst de werkregisters op 0 zetten
lus1:
mov mov mov mov inc djnz
a,#00h ;gaan uitkomst op 0 zetten teller,#04h ;er zijn 8 registers r0,#werkhh ;r0 als pointer naar werkhh @r0,a ;naar geheugen schrijven r0 ;pointer aanpassen teller,lus1 ;herhaal 4 keer
91 ; ; ; ; ; ; ; ;
we testen de bits van get1 van rechts naar links. Telkens een bit 1 is tellen we het 32 bit getal werkhh, werkh, werkl, werkll, get2hh, get2h, get2l, get2ll op bij het 32 bit getal uithhhh, uithhh, uithh, uith, uitl, uitll, uitlll, uitllll. Na elke bittest wordt het 32 bit getal werkhh, werkh, werkl, werkll, get2hh, get2h, get2l, get2ll met 2 vermenigvuldigd. omdat het over grote getallen gaat worden lussen gebruikt de operatie moet 32 keer uitgevoerd worden mov
telbit,#32
;totale lusteller
; we schuiven getal1 1 plaats naar rechts op om die bit te ; testen lus6: lus2:
mov mov mov rrc mov inc djnz
teller,#04h r0,#get1hh a,@r0 a @r0,a r0 teller,lus2
;gaat 0ver 4 bytes ;adres hoogste byte ;byte lezen ;rechts schuiven ;terug naar het geheugen ;pointer aanpassen ;herhaal voor 4 bytes
; in de carry zit de uiterst rechtse bit van get1. Die gaan we ; testen. Als die 1 is moeten we getal2 en de werkregs bij uit ; optellen. Anders niet jnc
lus4
;niet bijtellen dan lus4
; hier staat een 32+32 bit som.
lus3
mov mov mov clr mov addc mov dec dec djnz
teller,#08h r0,#get2ll r1,#uitllll c a,@r0 a,@r1 @r1,a r0 r1 teller,lus3
;gaat over 8 bytes ;adres laagste byte ;adres laagste byte ;carry moet op 0 staan ;som uitvoeren ;bijtellen ;naar uit schrijven ;pointer aanpassen ;pointer aanpassen ;herhaal voor 8 bytes
; De som is klaar. Nu moet get2 met twee vermenigvuldigd worden. ; Bij elke vermenigvuldiging wordt het getal 1 bit groter. ; Om dit op te vangen gebruiken we de 4 werk registers. Lus4:
mov mov clr
teller,#08h r0,#get2ll c
;lusteller ;adres laden laagste byte ;deze bit wordt ingeschoven
92 lus5:
mov rlc mov dec djnz
a,@r0 a @r0,a r0 teller,lus5
;getal lezen ;maal 2 ;terug schrijven ;pointer aanpassen ;klaar
djnz
telbit,lus6
;32 keer herhalen
; in de uit registers is het 64 bit resultaat beschikbaar. Het volgende programma laat een deling van twee 32 bit getallen zien. Het is opnieuw een onderdeel van een groter programma. We gaan er van uit dat het te delen getal get2 is en de deler is get1. Na de bewerking staat de uitkomst in uit en de rest in de werk registers. get1hh get1h get1l get1ll
equ equ equ equ
80h 81h 82h 83h
;adres hoogste byte get1 ;lagere byte ;volgende lagere ;laagste
werkhh werkh werkl werkl l
equ equ equ equ
84h 85h 86h 87h
;4 werkregisters ;bevatten rest na bewerking
get2hh get2h get2l get2ll
equ equ equ equ
88h 89h 8ah 8bh
;zie hierboven maar get2 ;idem
uithh uith uitl uitll
equ equ equ equ
8ch 8dh 8eh 8fh
;32 bit uitkomst
tussenhh tussenh tussenl tussenll
equ equ equ equ
90h 91h 92h 93h
;bewaren tussenresultaat
teller telbits
equ equ
20h 21h
;lusteller ;bitteller
; eerst gaan we de werk registers op 0 zetten. De uit registers ; moeten niet op 0 staan omdat ze gevuld worden tijdens de ; berekening.
93
lus:
mov mov mov mov inc djnz
teller,#04h r0,#werkhh a,#00h @r0,a r0 teller,lus
;het gaat over 4 bytes ;die vanaf dit adres staan ;ze moeten op 0 komen ;in geheugen stoppen ;volgende nemen ;4 keer uitvoeren
; de bitteller wordt gebruikt om te tellen hoe dikwijls het ; gehele programma doorlopen wordt. Dit is evenveel keer als er ; bits zitten in het te delen getal (nu 32) mov ; ; ; ; ; ; ; ;
telbits,#32d
;aantal keer lus doorlopen
bij de deling gaan we het te delen getal in de werk registers shiften (1 bit per keer dat de lus doorlopen wordt). Dit nieuwe getal wordt vergeleken met de deler. Is de inhoud van de werk registers kleiner, dan wordt in de uit registers een 0 geshift. Is de inhoud van de werkregisters groter, dan wordt van werk de deler afgetrokken. In de uit registers wordt een 1 geshift. Op die manier voeren wij ook een staartdeling uit.
; het te delen getal wordt een bit opgeschoven naar links in de ; werk registers lus6: lus1:
; ; ; ; ;
mov mov mov rlc mov dec djnz
teller,#08h r0,#get2ll a,@r0 a @r0,a r0 teller,lus1
;het gaat over 8 bytes ;hiermee starten ;getal lezen ;shiften ;opnieuw opslaan ;naar volgende wijzen ;8 keer doen
we gaan kijken of het getal in de werk registers groter is dan de deler. Is dat zo, dan komt het verschil in de werk registers te staan. Omdat we dat niet op voorhand weten, gebruiken we de tussen registers om het tussenresultaat op te slaan.
lus2:
mov mov mov mov clr mov subb
teller,#04h r0,#werkll r1,#get1ll b,#tussenll c a,@r0 a,@r1
;aantal bytes te bewerken ;adres brongetal ;adres deler ;adres tussen ;carry op 0 ;byte ophalen ;verschil berekenen
94 ; we hebben maar twee pointers. Daarom effe de adressen ; van plaats veranderen. xch xch mov
a,b a,r0 @r0,b
;inhoud omruilen ;inhoud omruilen ;uitkomst wegschrijven
; adressen terug op hun plaats zetten xch xch
a,r0 a,b
; adressen aanpassen dec dec dec
r0 r1 b
; moeten we 4 keer doen djnz
teller,lus2
; als de carry op 1 staat was werk
lus4
;als carry 1 naar lus4
; we copieren de inhoud van de tussen registers naar de werk ; registers.
lus3:
mov mov mov mov mov dec dec djnz
teller,#04h r0,#werkll r1,#tussenll a,@r1 @r0,a r0 r1 teller,lus3
;gaat over 4 bytes ;adres bestemming ;adres bron ;ophalen tussen ;schrijven naar werk ;pointers aanpassen ;4 keer herhalen
; als de carry op 1 staat was werk
cpl mov mov mov rlc mov
c teller,#04h r0,#uitll a,@r0 a @r0,a
;moeten omgekeerde hebben ;moeten 4 bytes shiften ;adres bestemming ;byte lezen ;carry inshiften ;terug wegschrijven
95 dec r0 djnz teller,lus5 ; dit moeten we 32 keer herhalen djnz
;pointer aanpassen
telbits,lus6
2.10.9 VIERKANTSWORTEL BEREKENEN: In sommige toepassingen kan het nodig zijn de vierkantswortel te berekenen. In onderstaand voorbeeld leggen we uit hoe dat voor een geheel decimaal getal kan uitgevoerd worden. We wensen de wortel te berekenen van 66564. We verdelen het getal in groepjes van twee digits. We gebruiken een “,” om de groepjes aan te geven. ____________ \/ 6,65,64 We zoeken van het eerste groepje het grootste getal waarvan het kwadraat kleiner of gelijk is aan dat groepje. In dit geval is dat 2 want 2*2=4 en 3*3 zou 9 geven, wat te groot is. __2_________ \/ 6,65,64 -4 --2 We schrijven de twee (is onderdeel van de uitkomst) bovenaan, nemen er het kwadraat van en trekken het van het groepje af. We laten het volgende groepje zakken. Het dubbele resultaat (2*2=4) zetten we voor het nieuw gevormde getal. We zoeken een getal d, zodat d*4d kleiner is of gelijk aan 265. 5 lijkt hier de juiste waarde want 5*45=225 __2_5_______ \/ 6,65,64 -4 --45 2 65 -2 25 -----40 We herhalen de vorige redenering (het dubbel van 25 is 50 en d*50d<=4046)
96 __2__5__8___ \/ 6,65,64 -4 --2 65 -2 25 -----508 40 64 -40 64 ------0 De uitkomst is 258. Meer uitleg kan http://www.nist.gov/dads/HTML/spuareRoot.html
je
vinden
op
Deze redenering gaan we toepassen op een binair getal. Het voorbeeldprogramma neemt de wortel van een 8 bit geheel getal. Om de berekening uit te voeren gebruiken we een aantal hulpregisters. Bron werk probeer uit tel
bevat het getal waar we de wortel willen van berekenen daarin shiften we het brongetal per 2 bits bevat de waarde van uit *4 +1 zal de uitkomst bevatten lusteller=aantal bits/2
Het programma is geschreven als een onderdeel van een ander programma. We gaan er van uit dat in bron reeds het getal staat waar we de wortel willen van berekenen. mov mov mov
werk,#00h uit,#00h tel,#04h
;op 0 zetten ;idem ;bron is 8 bits groot
; eerst zetten we in werk twee bits van bron lus:
mov a,bron rlc a mov bron,a mov a,werk rlc a mov werk,a mov a,bron rlc a mov bron,a mov a,werk rlc a mov werk,a ; nu in probeer uit *4 +1
;in accu om te shiften ;eerste bit ;terug bewaren ;inschuiven ;in accu om te shiften ;tweede bit ;terug bewaren ;inschuiven
97 mov rl setb rlc mov
a,uit a c a probeer,a
;in accu klaar maken ;*2 ;carry inshiften=*2+1 ;klaar ;in register schrijven
; we gaan kijken of het in werk kan mov clr subb jc
a,werk c a,probeer lus1
;gaan verschil berekenen ;mag niet meespelen ;carry=0 ok ;carry 1 zie verder
; de carry is 0. Werk wordt vervangen door het berekende ; verschil. In uit wordt een 1 ingeschoven mov mov setb rlc mov ljmp
werk,a a,uit c a uit,a lus2
;verschil in werk ;moet 1 in komen ;*2+1 ;terug op zijn plaats
; de carry was 1, het verschil mag niet in werk. In uit moet een ; 0 ingeschoven worden lus1:
mov rl mov
a,uit a uit,a
;moet 0 ingeschoven worden ;*2 ;terug op zijn plaats
; er is een stap van de itteratie uitgevoerd. Dat moeten we ; herhalen tot tel=0 lus2:
djnz
tel,lus
;herhaal
; de berekening is klaar. In uit staat de wortel 2.11 REFERENTIES NAAR DE WEBSITE: Voorgaande tekst is een samenvatting van volgende document: CD-8051\8051general\8051_programmers_guide.pdf Op het internet kan je tal van application notes vinden over binaire berekeningen. DEZE APPLICATION NOTES ZIJN NIET STEEDS 100% FOUTLOOS. NEEM ZE NIET KLAKKELOOS OVER!!!
98
HOOFDSTUK 3: INPUT/OUTPUT 3.1 INLEIDING: Input en output staat voor een brede waaier van hardware schakelingen die toelaten om gegevens van en naar de CPU te transporteren. Ze vormen een interface die de signalen op twee manieren aanpast, zodat ze in de SFR's beschikbaar kunnen zijn. De eerste manier vormt de signalen (spanningen, stromen, weerstand, …) die gebruikt worden om te meten, te regelen of te sturen, om naar de toegelaten spanningen voor de controller. Meestal worden de TTL specificaties gebruikt wanneer een signaal door digitale elektronica verwerkt moet worden. Er zijn ook de CMOS specificaties, en hoe langer hoe meer worden standaarden op lage spanning gebruikt (3.3 V en lager). Voor analoge signalen wordt meestal het 0-5V bereik gebruikt (dit is sterk afhankelijk van de gebruikte voedingsspanning en de inwendige referenties). Het overschrijden van de elektrische specificaties heeft voor gevolg dat de component niet langer naar behoren zal werken. STATISCHE ELEKTRICITEIT EN TRANSIENTEN zijn de meest voorkomende oorzaken van beschadigingen (naast het verkeerd gebruiken van de component). Inwendig in de chip is er enkel sprake van logische signalen (1 of 0), behalve voor de AD/DA omvormers. Deze interface is door de gebruiker te ontwerpen en te bouwen. Vele fabrikanten maken die taak eenvoudig door kant en klare interfaceschakelingen op de markt te brengen (TEXAS INSTRUMENTS, NATIONAL SEMICONDUCTOR, MAXIM, SGS, …). Soms is het echter noodzakelijk zelf een ontwerp te maken. De tweede manier is functioneel. Er zijn immers verschillende vormen van signalen die ingelezen of uitgestuurd moeten worden. De belangrijkste vormen van I/O zijn: parallelle I/O seriële I/O timers/counters/pulsgeneratoren A/D (analoog/digitaal omvormers) D/A (digitaal/analoog omvormers) andere… Alle functies die via een SFR geactiveerd worden, kunnen als I/O bekeken worden. Behoren niet tot de I/O, maar worden wel via de SFR's aangestuurd zijn de WATCHDOG en POWERCONTROL. De watchdogtimer is een hardware schakeling die toelaat om de werking van het programma te bewaken.
99 Powercontrol laat toe om het stroomverbruik van de CHIP in te stellen. Ook enkele speciale CPU registers behoren tot de SFR’s: a, b, dph, dpl, sp, psw. In de volgende paragrafen worden de basis I/O mogelijkheden verder toegelicht (voor details zie datasheet ADuC832). 3.2 PARALLELLE I/O: Zoals reeds vroeger vermeld, is een microcontroller een single-chip computer. Omdat zowel I/O als het geheugen "aan boord" zijn, is er geen behoefte voor externe bussen. Bijna alle pinnen van het IC kunnen gebruikt worden voor het inlezen, of uitsturen van gegevens (behalve voeding, klok en enkele controle signalen). De I/O pinnen worden per 8 samen genomen in een SFR. Elke groep van 8 wordt een poort genoemd. Omdat een controller meer dan een poort heeft, worden de poorten genummerd P0, P1, …. Elke pin van een poort krijgt een nummer, dat overeenkomt met de bitpositie in de SFR: P0.0, P0.1, … (px.7 is de msb en staat links in een byte). De poorten zijn bit adresseerbaar, zodat een pin een bit adres heeft, dat rechtstreeks met bepaalde instructies geadresseerd kan worden. Zo wordt het mogelijk om met 1 instructie een pin te complementeren, te setten, als test te gebruiken voor een sprong,… Onze controller beschikt over 4 poorten: Poort0: wordt gebruikt voor het inlezen van 8 schakelaars (DIP SWITCH). Ze zijn ook beschikbaar op Header 0 voor andere toepassingen. Poort1: wordt gebruikt als analoge of digitale input. mogelijkheden verwijzen we naar het schema van de kaart.
Voor
de
Poort2: wordt op de kaart gebruikt 0m 8 LED's aan te sturen. Via Header 2 zijn ze ook beschikbaar voor andere toepassingen. De LED's kunnen met jumper J5 uitgeschakeld worden. Poort3: wordt gebruikt voor het inlezen van 4 schakelaars en de seriële poort. De signalen zijn beschikbaar op Header 3 voor andere toepassingen. Merk op dat na reset de poorten op 1 staan. Dit impliceert dat er enkel actief laag gewerkt kan worden. Verder zullen we zien dat er elektrisch een noodzaak is om actief laag te werken. Alle 8051 uitvoeringen hebben een heel speciale poortstructuur. Hiermee bedoelen we de elektronica die de pin stuurt, en de manier waarop de poort gebruikt kan worden. In onderstaande figuren wordt de basisstructuur van een poort weergegeven, en de laatste FET’s die de pin sturen.
100
Om de werking van het schema beter te begrijpen, moet je weten dat na
101 reset alle signalen op 1 staan. Dus ook alle pinnen van de controller staan na reset op 1. Het programma zal die toestand moeten wijzigen. Een poort wordt via zijn SFR aangestuurd. Omdat de controller nog andere I/O functies bezit, naast de PIO, moet er ook een manier zijn om die signalen in en uit het systeem te transporteren. Dit gebeurt ook via de poorten, en worden ALTERNATIEVE FUNCTIES genoemd. Ook alternatieve functies zijn na reset uitgeschakeld, en staan op 1 (zie onderstaande figuur).
Laten we eerst de uitgangstrap onder de loep nemen. Opvallend is de OPEN DRAIN (open collector) structuur van de pin. Hierdoor is het overbodig om in te stellen of de poort als input of als output gebruikt zal worden. Na reset staat de poort op 1, en is het de pull-up weerstand die voor het hoog signaal zorgt. Het is niet moeilijk om met een extern signaal de pin laag te maken, zodat de pin als input gebruikt kan worden. De pull-up is heel zwak, zodat hiervoor niet veel stroom nodig is (uA). Wordt de pin niet door een extern signaal aangestuurd, kan met software de pin laag gemaakt worden. Dit laag signaal kan wel veel stroom leveren (mA), zodat het niet langer mogelijk is om de poort extern te sturen (interne dissipatie). De poort werkt dan exclusief als output. De
102 programmeur zal de poort op 1 zetten als een uitgangssignaal 1 nodig is, of als de poort als input gebruikt wordt. Een 0 mag enkel naar de poort geschreven worden als de poort als output werkt. Laten we even aannemen dat de poort als output gebruikt kan worden. We zien dat de open collector aangestuurd wordt door een and functie. Die werkt als een OR op actief lage signalen. Dit wil zeggen, dat als de alternatieve functie niet werkt (hoog is), de uitgang bepaald wordt door de uitgang van de latch, die op zijn beurt via de poort-SFR gestuurd wordt. Is de latch hoog, dan kan de alternatieve functie naar buiten. Je kan de poorten vrij gebruiken, zolang je geen alternatieve functies activeert. Doe je dat wel, dan moet je nagaan welke beperkingen er zijn bij het gebruik van de poorten. Bij het schrijven van de poort-SFR, wordt de uitgang aangestuurd. Bij het lezen van de SFR zijn er twee mogelijke bronnen die de informatie leveren. Sommige instructies lezen de spanning op de pin, andere lezen de spanning op de latch. De leeswaarde van de SFR is dus afhankelijk van de gebruikte instructie. De instructies die de latch lezen, zijn de READ MODIFY WRITE INSTRUCTIONS. Zij zijn opgenomen in onderstaande tabel
De alternatieve functies lezen steeds de spanning van de pin. Wordt een pin als input gebruikt voor een alternatieve functie, dan kan ze gelijktijdig als input voor de poort gebruikt worden. Het is zelfs mogelijk om de poort als output te gebruiken, en gelijktijdig als input voor een alternatieve functie te gebruiken. De parallelle interface is de meest gebruikte, omdat het gros van alle
103 stuur- en controlesignalen een aan/uit karakter hebben. Het is de meest eenvoudige, omdat er geen manipulaties gebeuren bij het inlezen, of het uitsturen van de gegevens. Tenslotte wensen we nog op te merken dat de controller niet de nodige hardware heeft om parallelle communicatie toe te laten (printer interface). Dit wil niet zeggen dat deze vorm van communicatie niet kan gebeuren, maar dat je de controlesignalen die hier bij horen met software zal moeten nabootsen. Voor het gebruik van poort 1 (AD inputs) en de DA outputs raadpleeg je best de datasheet van de ADuC832. Voorbeelden: Hieronder staat een programma dat op poort 2 een binaire teller laat zien waarvan de snelheid instelbaar is met de schakelaars van poort 0.
loop2: loop1: loop:
org mov mov mov mov djnz djnz djnz inc ljmp
0000h p2,#00000000b ;binaire teller op 0 zetten r1,#02h ;start waarde delay r2,#00h ;start waarde delay r3,p0 ;schakelaars inlezen r3,loop ;p0*12µs r2,loop1 ;256*(p0*12µs+12µs+6µs) r1,loop1 ;2*(256*(p3*12µs+18µs)+12µs) p2 ;binaire teller +1 loop2 ;blijf herhalen
Het volgende programma is een looplicht met variabele snelheid. P0.2 wordt gebruikt om de richting van het looplicht in te stellen. org loop2: loop1: loop:
00000h
mov mov mov mov orl
a,#00000001b r1,#02h r2,#00h r3,p0 03h,#00000111b
djnz djnz djnz rl jnb rr
r3,loop r2,loop1 r1,loop1 a p0.2,loop3 a
;startwaarde looplicht ;start waarde delay ;start waarde delay ;schakelaars inlezen ;laagste bits buiten spel ;03h=r3 bij regbank 0 ;p3*12µs ;256*(p3*12µs+12µs+6µs+12µs) ;8*(256*(p3*12µs+6µs+12µs)+12µs) ;data 1 plaats schuiven ;p3.2 geeft de richting
104 loop3:
rr a mov p2,a ljmp loop2
;LED’s aanpassen ;blijf herhalen
Tenslotte verwijzen we naar de datasheet van de ADuC832 voor een uitvoerige beschrijving van de elektrische karakteristieken van de poort. Dit is enkel van belang voor studenten die een controller gaan gebruiken in eigen schakelingen. De beschreven poortstructuur is immers niet bij alle 8051 derivaten exact gelijk. Soms is het mogelijk om meer stroom te sturen. Soms is de structuur compleet verschillend zoals bij de C868 controller. Ga na wat het doel is van de drie mosfets die samen de pull-up weerstand vormen (zie technical manual). 3.3 SERIELE INTERFACE: Bij de parallelle interface worden 8 bits gelijktijdig beschikbaar gesteld op evenveel aansluitingen. Als we op die manier gegevens over een grotere afstand willen transporteren, zullen we een dure kabel nodig hebben (minimaal 9 geleiders). Bovendien zijn TTL specificaties niet geschikt om een signaal over meer dan enkele tientallen cm te transporteren. Er is een interface (LEVEL SHIFTER) nodig die het signaal elektrisch aanpast. Voor acht geleiders is dit een volumineuze toestand. Aangezien er bij parallelle communicatie nog controle signalen gebruikt worden betreft het meer dan acht (10-16) geleiders. Er zijn twee soorten seriële communicatie: SYNCHROON en ASYNCHROON. Beide types kunnen door de controller gebruikt worden. De synchrone mode wordt minimaal ondersteund, en wordt hier niet besproken. De asynchrone mode laat toe om zowel 8 als 9 bits te verzenden. Die Negen Bit Mode (NBM) laat toe om eenvoudig meerdere processoren via dezelfde verbinding te laten communiceren. We beperken ons hier tot de normale 8 bit communicatie. In onderstaande figuur wordt het BYTE FORMAT weergegeven (ook wel frame format genoemd).
105
De snelheid van de communicatie kan ingesteld worden, en noemt men de BAUD RATE. Bij 9600 baud, worden er 9600 bits per seconde verzonden. Zowel de zender als de ontvanger moeten op dezelfde snelheid ingesteld worden. Per byte worden er nog twee bits toegevoegd, die noodzakelijk zijn om de aanvang en het einde van en byte te kunnen
106 herkennen. Elk frame bestaat dan uit 10 bit (1+8+1). Er worden dan 960 bytes/seconde verzonden bij 9600 baud. Bij SIO zijn er drie mogelijke manieren om de baudrate in te stellen. Bij de 8051 moet timer0 gebruikt worden om de baud rate in te stellen. Dit kan enkele als een baud rate kristal gebruikt wordt (bvb: 11.0592MHz). Omdat dit niet steeds de beschikbare werkfrekwentie is, kan bij de ADuC800 ook timer 2 als baud rate generator gebruikt worden. De meest flexibele manier is het gebruik van timer 3. Die is uniek voor de ADuC8xx en kan met de standaard klok (een uurwerk kristal) en de PLL zowat alle courante baud rates aan. Niet alle asynchrone communicaties verlopen volgens het "10 bit per frame" formaat. Bij het maken van een verbinding moet zowel het frame format, de baud rate, als de gebruikte level shifters nagegaan worden. Bij de level shifters zijn er verschillende mogelijkheden, die elk hun toepassingsgebied hebben. We overlopen kort de meest gebruikte systemen bij asynchrone communicatie.
TTL: Dit zijn de specificaties van het signaal, zoals ze de chip verlaten. Op deze manier kunnen we gegevens versturen over korte afstanden (1.5m). Omdat het hoog signaal door de pull-up weerstand geleverd wordt, zal het heel moeilijk zijn die afstand te halen (0.5m). Signalen worden onder deze vorm gebruikt wanneer meerdere controllers dicht bij elkaar zitten (in een rack).
RS232: Deze norm is voorzien voor communicatie met een MODEM, maar de spanningen en stromen die door deze norm bepaald zijn, worden veelvuldig gebruikt bij "3-draads" verbindingen (TxD, RxD, GND). Een 0 wordt verstuurd als een positieve spanning tussen 3 en 25 volt. Een 1 als een negatieve spanning tussen -3 en -25 volt. Door deze spanningen te gebruiken kan de afstand verlengd worden tot 15m. In de praktijk zijn afstanden tot >100m mogelijk. De interface is een spannings interface, wat inhoud dat de ontvanger zo weinig mogelijk stroom zal vragen aan de zender. De ontvanger heeft een hoge impedantie. Daardoor is het niet moeilijk om fouten te induceren op de verbindingsdraden. Voor grotere afstanden en/of communicatie in een niet storingsvrije omgeving wordt dan ook een andere level shifter (interface) gebruikt. Omdat de RS232 interface populair was bij verbindingen tussen computers, zijn er IC's op de markt gekomen, die zelf de hogere positieve en negatieve spanningen genereren die nodig zijn voor de verbinding. Deze low cost single chip level shifters maken de
107 interface nog populairder (vb. MAX232).
RS422/RS485: Deze interface werd ontworpen om een snelle communicatie over grotere afstanden mogelijk te maken (1000m, 100Mbit/s). Een driver heeft twee uitgangen, die elkaars inverse zijn. De spanning op een uitgang kan drie toestanden aannemen: hoog (5V), laag (0V) of hoog impedant (busverbindingen). De ontvanger werkt differentieel, zodat een verschilspanning van 200mV voldoende is om het signaal te herstellen. Om de interface tot zijn recht te laten komen moet een TWISTED PAIR CABLE gebruikt worden (UTP kabel is unshielded twisted pair draad).
CURRENT LOOP: Het signaal wordt omgezet naar een stroom. Door een stroombron in en uit te schakelen wordt de informatie verzonden. Omdat de weerstand van de geleiders onbelangrijk is, kan de informatie over heel grote afstanden verzonden worden. De kostprijs van currentloop (afzonderlijke spanningsbron voor de tussenkring), de lagere snelheid (kHz, beperkt door optocouplers) en het volume van de interface maken dat het systeem nog slechts in een beperkt aantal toepassingen gebruikt wordt. De voordelen zijn de hoge beschermingsgraad voor de toestellen die verbonden worden en de ongevoeligheid voor storingen.
OPTISCHE VERBINDINGEN: Dit type communicatie zal in de nabije toekomst het gros van de verbindingen uitmaken. De belangrijkste huidige beperkingen zijn kostprijs en afmetingen van de interface. Door licht te gebruiken in glasvezel kabels ben je ongevoelig voor interferentie. Afstanden en snelheden zijn bijna onbeperkt.
ANDERE: Seriële communicatie is heel belangrijk, en in tal van toepassingen noodzakelijk. Heel wat fabrikanten brengen dan ook eigen standaarden uit. Het met elkaar verbinden van twee toestellen is dan ook niet steeds evident. Voor sommige toepassingen worden zelfs standaarden opgezet om de concurrent buiten spel te zetten. Enkele andere normen zijn: CAN, SINEC, IIC-BUS, FIELD-BUS, FIP-BUS, BIT-BUS, ....
Tenslotte merken we op dat de synchrone verbindingen tussen grotere computers (PC’s) de asynchrone zullen verdringen. Hierbij denken we vooral aan USB en ethernet. V2 van het ADuC832 bord is uitgerust met een protocol-vertaal IC. Het zet het asynchrone protocol om in een USB verbinding met de PC. In de PC gebeurt het omgekeerde. Zo lijkt het alsof we met de PC een
108 asynchrone verbinding maken, maar in realiteit gebeurt de dataoverdracht via een USB verbinding. Voor de gebruiker verloopt dit transparant (het is alsof de USB omzetting er helemaal niet is). LET OP: De PC creëert een COM poort wanneer je de USB verbinding maakt. Elke COM poort krijgt een nummer. Dat nummer is afhankelijk van de gebruikte USB poort op de PC, en de gebruikte controller kaart. Het nummer van die COM poort moet ingesteld worden in het download programma (of in hyperterminal(Tera Term)). Anders kan er geen verbinding gemaakt worden. Gebruik je altijd dezelfde kaart, en dezelfde USB aansluiting, dan krijgt de COM poort steeds hetzelfde nummer. In de volgende paragrafen overlopen we de SFR’s van de SIO. Het sbuf register is een SFR die gebruikt wordt om data naar de seriële interface te schrijven. Die data wordt dan door de hardware verzonden. Als de hardware een byte ontvangen heeft, wordt die op zijn beurt in het sbuf register beschikbaar. Het instellen van de gewenste werkmodus gebeurt via het scon register.
109
Het instellen van de communicatiesnelheid gebeurt met timer 1, 2 of 3. We beperken ons hier tot timer 3 als baud rate generator. In de onderstaande figuren is duidelijk weergegeven hoe de baud rate generator werkt.
Het t3con register moet geladen worden in functie van de gewenste baud rate. De formules en een berekeningsvoorbeeld kan je terugvinden in de datasheet.
110 In onderstaande tabel zijn voor enkele courante baud rates de waarden van de fractional devider en het t3con register weergegeven. De fractional devider zit in het SFR t3fd.
Voorbeeld: ; ; ; ; ;
dit programma zet eerst de seriële poort klaar om te zenden en te ontvangen (let op baud rate =19200 i.p.v. de 9600 in het labo) na de ontvangst van een spatie=blank wordt een beep verzonden. Andere karakters hebben geen gevolg
beep blank
equ equ
07h 20h
org
0000h
;ascii code van een beep ;ascii code blank=spatie
;We initialiseren de seriële poort op 19200 baud, 8 bit uart met ;variabele snelheid. mov scon,#0111**00b mov t3fd,#2dh mov t3con,#83h
;* mag 0 of 1 zijn ;zie tabel ;we gaan er van uit dat ;de systeemklok 2MHz is
111 ;de seriele poort kan nu gebruikt worden. De bits ti en ri ;kunnen gebruikt worden om te weten of er data is toegekomen, of ;dat de vorige data verzonden werd. Lus:
jnb
ri,$
clr
ri
mov a,sbuf cjne a,#blank,lus
;$ is het adres van de ;huidige lijn. We wachten op ;een inkomend karakter ;bit zelf laag maken na ;ontvangst ;waarde in accu laden ;indien blank terug wachten
; het karakter was een blank mov jnb clr ljmp
sbuf,#beep ti,$ ti lus
;beep verzenden ;wachten tot byte verzonden ;bit laag maken ;wachten op volgende ;karakter
3.4 TIMERS: Timers zijn fundamenteel niets anders dan digitale tellers. Het aantal bit van een teller geeft aan tot welke waarde de teller kan gaan zonder een OVERFLOW te genereren. Een 8 bit teller kan tot 256 tellen, een 16 bit tot 65536. Worden de tellers gevoed met een afgeleide van de systeemklok, dan zullen ze tegen een vaste snelheid tellen (tussen elke puls is er een vaste, gekende periode) en spreken we van timer werking. Worden de tellers gevoed met een extern signaal, ongeacht of het een constante frequentie heeft, spreken we van counter werking. Merk op dat de werking van de teller in beide gevallen bijna identiek is, behalve dat de herkomst van het te tellen signaal verschillend is. Bij de "oude" 8051 zijn er slechts twee timer/counter circuits aanwezig: timer0 en timer1. Beide timers zijn 16 bit groot. Omdat timer1 gebruikt kan worden als baud rate generator, kan timer0 opgesplitst worden in twee 8 bit timers, maar waarvan de mogelijkheden gereduceerd zijn t.o.v. de originele timers. In de volgende figuren geven we de verschillende mogelijkheden van deze twee timers weer. We beperken ons tot de twee modes die in het labo gebruikt worden. Voor de andere mogelijkheden van deze timers, voor timer2, voor de timer interval counter en de PWM
112 units verwijzen we naar de datasheet van de ADuC832. De werking van timer1 is identiek aan die van timer0. Mode 1 geeft een 16 bit timer/counter. Het is nu mogelijk om 65536 pulsen te tellen alvorens een overflow optreedt. In de counter werking komt dit overeen met 31,25ms, omdat de systeemklok 2.097152MHz is (zie onderstaande figuur voor mode 1). De teller bestaat uit twee 8 bit SFR’s die in serie geschakeld worden om samen een 16 bit teller te vormen. TH0 is de hoge byte van de teller, TL0 de lage byte. De control lijn zal bepalen of de teller al of niet signaal krijgt om te tellen. Afhankelijk van de waarde van de C/T bit komt dat signaal van een externe pin (klem ic) of van de inwendige systeemklok/12. De waarde van de gate bit zal bepalen of de int0 pin gebruikt wordt om de teller aan/uit te zetten. De tr0 bit is een soort van algemene schakelaar voor het aan/uit zetten van de teller. Gating wordt enkel gebruikt om de periode van een extern signaal te meten. Zolang dat hoog is zal de teller tellen. Wordt het laag stopt de teller. De pin int0 kan met software getest worden om na te gaan of de periode voorbij is en de waarde uit de teller gelezen mag worden. De pin is ook in staat om een interrupt te genereren (zie hoofdstuk 4).
In de 16 bit mode is het bijna onmogelijk om evenementen nauwkeurig af te passen. De noodzaak om de timer even af te zetten om een nieuwe
113 waarde in te laden, maakt het mogelijk dat er evenementen verloren gaan. Dit kan voorkomen worden in de mode 2. In deze mode (zie onderstaande figuur) wordt een reload systeem gebruikt. Het herladen van de timer gebeurt zonder dat de timer even af gezet wordt.
De controlebits en signalen hebben in mode 2 dezelfde werking als in mode 1. De teller zelf is nu maar 8 bit groot en bestaat uit TL0. TH1 wordt tijdens de initialisatie van de timer geladen met een reload waarde. Telkens TL0 groter wordt dan 0ffh (0ffh+1 geeft overflow), zal het TL0 register herladen worden met de inhoud van TH0. Hierdoor kunnen wij bepalen vanaf welke waarde de teller start met tellen. Dit is handig voor repetitieve metingen: doe alle 24 telbeurten dit, doe na 200 uS dat,…. Het instellen van de timers gebeurt via twee controle registers die in volgende figuren zijn weergegeven:
114
Het tmod register wordt gebruikt om de mode in te stellen. Het tcon register bevat de overflow flag en de timer run bit. Merk op dat enkel het
115 tcon register met bit instructies aangepast kan worden. Dit is belangrijk omdat de grijze bits andere functies kunnen activeren. Bij het tmod register moeten byte instructies toegepast worden. Let dus op bij het laden dat de andere timer geen foutieve initialisatie krijgt. In onderstaande tekst zullen we enkele begrippen verklaren die veelvuldig gebruikt worden Een teller (ongeacht timer of counter werking) verhoogt per inkomende puls de oude tellerwaarde met een. Wordt de maximale tellerwaarde overschreden, dan treedt er een OVERFLOW op. Om aan te geven dat dit gebeurt is, wordt een OVERFLOW FLAG gezet. Indien de gebruiker het wenst, kan deze vlag een interrupt genereren naar de cpu (zie hoofdstuk 4). In de meeste gevallen wordt de vlag automatisch gereset NA het verwerken van de interrupt. Worden er geen interrupts gegenereerd, dan blijft de vlag staan (pending interrupt). De overflow flag kan door de gebruiker gelezen en eventueel aangepast worden (zowel setten als resetten). De waarde van de teller na de overflow is afhankelijk van de mode waarin de timer staat. Werd een RELOAD MODE ingesteld, dan wordt de startwaarde na overflow bepaald door de inhoud van een reloadregister. Een speciale timer, de compare/capture timer kan enkel in deze mode werken. Timer/counter circuits worden vooral voor gebruikt: afpassen van tijd tellen van pulsen meten van de tijdsduur van een puls genereren van pulsen
volgende
toepassingen
Het afpassen van een tijd kan op twee manieren gebeuren. Als het de bedoeling is om een uurwerk te maken, dan volstaat het de timer in reload mode te zetten, en na een bepaald aantal overflows, die we via interrupt kunnen tellen, is de gewenste tijd voorbij. In vele gevallen willen we een tijd afpassen na een externe gebeurtenis. In dat geval wordt de timer gestart na een externe interrupt of na polling. Bij het tellen van pulsen wordt de teller als counter ingesteld. Door de overflow-interrupts te gebruiken kan het telbereik groter worden dan de maximale inhoud van de teller. In de interrupt routine wordt dan een register met 1 verhoogd. Bij het meten van de tijdsduur van een puls zijn er verschillende mogelijkheden: We kunnen gebruik maken van polling. De nauwkeurigheid is dan volledig afhankelijk van het programma.
116
Door het signaal aan te leggen op interruptingangen, kan het starten en stoppen van de timer onafhankelijk zijn van het hoofdprogramma. Ook hier is de nauwkeurigheid bepaald door de snelheid waarmee de CPU op het interrupt signaal reageert.
Het gebruik van gating is bij timer0 en timer1 mogelijk. Voorbeelden: ; We wensen een signaal dat op p3.2 binnen komt 10ms later naar ; buiten te sturen op p3.3 . alle signalen zijn actief hoog ; (waar=1). Beide signalen worden samen non actief.
lus:
org
0000h
clr jnb mov mov mov
p3.3 ;p3.3 op 0 p3.2,$ ;blijf wachten tot p3.2 = 1 tl0,#2ch ;65536-1748 inladen (timer th0,#f9h ;telt 5.7uS tmod,#00000001b ;16 bit mode timer0
; we gaan er van uit dat timer 1 niet gebruikt wordt. Is dit wel ; het geval, dan zullen de eerste 4 bits een bepaalde waarde ; hebben. setb jnb clr setb clr jb ljmp
tr0 tf0,$ tf0 p3.3 tr0 p3.2,$ lus
;timer starten ;wacht tot overflow ;als geen interrupt soft clearen ;uitgang activeren ;timer stoppen ;wachten tot bit terug 0 wordt ;cyclus herhalen (einde programma)
We wensen alle 24 pulsen op de t0-pin een uitgangspuls op p3.3 ; te genereren. De uitgangspuls duurt 12 uS. org
0000h
clr mov mov mov
p3.3 ;p3.3 op 0 tl0,#(256-24) ;reload waarde en tellerwaarde th0,#(256-24) ;laden in timer0 tmod,#00000110b ;8 bit auto-reload mode counter
; we gaan er van uit dat timer1 niet gebruikt wordt. De eerste 4
117 ; bits hebben dan geen belang. wacht:
setb jnb clr setb nop clr ljmp
tr0 tf0,$ tf0 p3.3
;timer starten ;wacht tot overflow ;als geen interrupt soft clearen ;output op 1 ;1 nop instructies (6us) ;output op 0 ;wachten tot 24 pulsen
p3.3 wacht
3.5 ANALOOG NAAR DIGITAAL OMVORMER:
Heel wat toepassingen gebruiken sensoren die een spanning aan hun uitgang leveren. De belangrijkste zijn temperatuur, druk, positie, stroom, …. De Analoog naar Digitaal omvormer laat toe om de “niet digitale” ingangsspanning om te zetten in een getal, dat een maat is voor de waarde van die spanning. Het bereik van de ingangsspanning is 0-5 volt. Het getal dat bij de omvorming gegenereerd wordt is begrepen tussen 0000H en 0FFFH. De RESOLUTIE van de AD is de kleinste variatie in ingangsspanning die gemeten kan worden. Ze wordt bepaald door het aantal bit van het resultaat. De AD omvormer van de ADuC832 heeft een 12 bit resultaat. De resolutie wordt dan: 2.5V/2**12= 2.5/4096= 0.00061 volt. De maximale spanning die gemeten kan worden is 4095*0.00061=2.4994V. Bij de ADuC832 kan een externe referentie naar keuze gebruikt worden (zie specs voor beperkingen). Hierdoor zijn andere meetbereiken mogelijk. In de onderstaande weergegeven.
figuur
wordt
de
transfert
karakteristieek
118
LET OP! De uitkomst van een omvorming is beschikbaar in adcdatah en adcdatal. In de hoogste vier bits (die worden niet gebruikt voor het resultaat) staat het nummer van het gelezen kanaal (zie onderstaande figuur).
119 De omvormer heeft een CONVERSION TIME die afhankelijk is van de sample tijd en de omvormingstijd. De ingangsspanning wordt GESAMPLED bij de start van de omvorming. De omvormer beschikt over een SAMPLE AND HOLD circuit dat een foto neemt van het ingangssignaal. Hierdoor mag de ingangsspanning veranderen tijdens de omvorming. De AD omvormer gebruikt een REFERENTIESPANNING of VAREF t.o.v. dewelke de te meten spanning vergeleken wordt. Inwendig is een referentie beschikbaar van 2.5 volt. De AGND of analoge grond, is de massa van het inkomende signaal, en wordt verbonden met de massa aansluiting van de omvormer. Vele controllers beschikken niet over een AD omvormer. In dat geval zijn er verschillende manieren om een analoog signaal te meten: externe AD omvormer U/F omvormer gebruik van een DA met comparator gebruik van een RC netwerk en een comparator gebruik van analoge schakelaars … In de figuur op de volgende bladzijde is een blokdiagramma van de AD omvormer weergegeven.
120 Het besturen gebeurt via drie controleregisters die in onderstaande figuren zijn weergegeven. Er kan maar 1 ingang per omzetting verwerkt worden. Na de multiplexer wordt het signaal door een sample&hold bemonsterd.
121
122
Voor het gebruik van de temperatuursensor gelieve volgende specificaties te gebruiken:
Voorbeelden: ; We willen op poort 1.0 een analoge spanning meten tussen 0 ; en 2.5 volt. De 12 bit meting wordt zichtbaar gemaakt op de ; lcd als een hexadecimale waarde. org
0000h
mov adccon1,#10011100b ;instellen aantal ; klokpulsen voor een omvorming, het sampelen en inschakelen adc mov adccon2,#00100000b ;continue omvorming ; kanaal0, geen interrupts lcall initlcd ;lcd scherm initialiseren
123 loop:
; lcd
mov lcall mov lcall
a,#ff outcharlcd a,adcdatah outbytelcd
;cursor vooraan de lijn ;naar lcd sturen als ascii ;hoge byte uitkomst lezen ;afdrukken als hex getal op
mov lcall ljmp
a,adcdatal outbytelcd loop
;idem lage byte ;herhalen
In de continue conversie mode worden de adcdata registers continu door de AD omvormer aangepast na elke meting. Bij de eenmalige omvorming gebeurt dit slechts na het starten en het beëindigen van een meting. De continue mode kan enkel gebruikt worden wanneer de data van de ADC door hardware (interrupt of DMA) wordt gebruikt. In alle andere gevallen moeten we de eenmalige mode gebruiken.
3.6 FAIL SAVE MECHANISMS: Als een gewone PC "vastloopt", dan schakelen we de spanning even uit, of bedienen de reset knop. Bij een ABS systeem in een auto, of een automatische piloot van een vliegtuig is dit niet zo evident. Vele systemen werken in afwezigheid van mensen, of hebben juist de bedoeling om de mens te beschermen. In al die gevallen, is het ontoelaatbaar dat de controller zichzelf niet kan controleren. Hoe komt het dat een controller vastloopt? Hiervoor zijn verschillende oorzaken mogelijk.
Slechte software is in een aanvangsfase de meest voor de hand liggende reden. Daar kan je weinig tegen beginnen, tenzij de normale debugging van een programma, en het testlopen van de toepassing.
Als er iets misloopt met de hardware, dan kan je daar weinig tegen beginnen. Nochtans zal de ADuC8xx enkele hardware fouten kunnen opvangen.
De belangrijkste oorzaak voor het vastlopen van de controller zijn: slechte voedingsspanning ESD problemen EMI problemen Een ontlading van statische elektriciteit kan voor gevolg hebben dat een bit van een instructie foutief gelezen wordt. De controller gaat foutieve
124 gevolgen trekken, en kan de applicatie beschadigen. Hoe doen we er iets aan? De PROGRAMMABLE WATCHDOG TIMER is een programmeerbare timer die door speciale instructies telkens opnieuw ingeschakeld moet worden. Gebeurt dit niet, dan loopt de timer af, en na de ingestelde tijd wordt de controller gereset, en start het programma opnieuw vanaf 0000H. Het systeem laat ook toe om beperkte hardware fouten op te vangen. De watchdog timer kan zowel hardware als software opgestart worden. Door hardware op te starten wordt vermeden dat corrupte software de watchdog niet eens meer zou starten, waardoor hij ook niet kan beveiligen. De watchdog kan ook gebruikt worden om de controller te interrupten. Hierdoor kan na het vastlopen van de controller het probleem in een interruptroutine opgevangen worden. Het systeem van de interrupt is vooral bedoeld om de chip wakker te maken uit een slaap mode. De software kan nagaan of een eventuele reset van een van beide watchdogs afkomstig is via onderstaand controleregister:
125
In sommige toepassingen is dit niet voldoende. In dat geval worden verschillende controllers gebruikt die elkaar controleren. In elk geval zal nog een externe spanningsbewaking nodig zijn. Die moet er voor zorgen dat BROWN OUT niet kan voorkomen. Als de voedingsspanning onder de specificaties daalt zal de controller onvoorspelbare resultaten geven. Zodra dat voorkomt spreekt men van brown out. De spanningsbewaking zal de controller resetten als de voedingsspanning een vooropgestelde drempel niet bereikt. Via onderstaand controleregister kan die bij de ADuC832 ingesteld worden.
126
3.7 BESLUIT: Dit hoofdstuk heeft tot doel enkele veel gebruikte begrippen te verduidelijken. Voor het gebruik en de programmatie van de I/O verwijzen we naar de datasheet van de ADuC832. Het is belangrijk dat de student zowel in de datasheet als bij de docent te rade gaat voor alle onduidelijkheden die zich tijdens het inoefenen voordoen. 3.8 REFERENTIES NAAR DE WEBSITE: Dit hoofdstuk was een beknopte behandeling van de standaard 8051 I/O en de ADuC832 specifieke I/O. In de datasheet kan je meer uitleg vinden over de werking en aansturing van de verschillende I/O bouwstenen in de controller.
127
HOOFDSTUK 4: INTERRUPT 4.1 INLEIDING: Een microcontroller wordt dikwijls gebruikt in I/O intensieve toepassingen. Het is heel belangrijk dat er snel gereageerd wordt op externe gebeurtenissen. Niet alleen externe gebeurtenissen komen in aanmerking, ook tijdsmetingen, seriële communicatie, AD-omvormingen,…. Algemeen spreekt men over REAL TIME uitvoering van een programma. Het begrip real time is gebonden aan de aard van het proces. Bij een PC is het proces de gebruiker. Zolang die niet merkt dat de PC meerdere taken uitvoert, is aan de real time voorwaarde voldaan. Bij microcontrollers gaat het meestal over snelle processen. Het is onmogelijk om aan die “real time” eisen tegemoet te komen met een eenvoudig lineair programma. Immers bij het afpassen van tijd in een lus, worden de ingangspinnen niet bekeken. Dikwijls is de uitvoeringstijd van het programma afhankelijk van de variabelen, zodat langetermijn tijdsmetingen onnauwkeurig worden. Vooral wanneer er verschillende taken door een microcontroller uitgevoerd moeten worden, komen die tekorten aan het licht. Interrupt is een oplossing voor dit probleem. Het principe van interrupt kan het best vergeleken worden met een hardware geinduceerde call naar een interruptroutine. Voor bepaalde hardware onderdelen van de controller (de I/O blokken), kan de gebruiker een speciale subroutine klaar zetten. De plaats waar ze in het geheugen moet staan is afhankelijk van de interruptbron (het I/O blok). De laatste instructie moet een RETI zijn. Bij de CPU en bij de betrokken I/O blokken moeten de interrupts toegelaten worden, waarna het systeem automatisch werkt. Als een toestand zich voordoet (vb. timer overflow, ontvangen van een serieel karakter, flank op een ingangspin,…), zal het I/O blok een signaal geven aan de CPU (interrupt). De CPU reageert hierop door de routine op het overeenkomstige adres op te starten. Hiertoe wordt het hoofdprogramma tijdelijk onderbroken. Is de interruptroutine klaar, dan wordt het hoofdprogramma verder gezet. De stack speelt een cruciale rol in het geheel. Hij wordt gebruikt voor het bewaren van het return_adres (plaats waar het hoofdprogramma werd onderbroken), maar ook voor het tijdelijk bewaren van de inhoud van de registers die door de interruptroutine gebruikt worden.
128 4.2 INTERRUPTBRONNEN: In de onderstaande figuur zijn alle interruptbronnen opgenomen. De tabel geeft ook de prioriteit aan (volgorde waarin twee gelijktijdige interrupts uitgevoerd worden).
De volgende tabel bevat de interrupt vectoren interruptroutine in het geheugen moet staan).
(adres
waar
de
Een interrupt wordt door hardware opgewekt. Omdat de cpu zijn interrupt verwerkende schakeling kan uitschakelen, of omdat meerdere interrupts gelijktijdig kunnen optreden, worden interrupts tijdelijk opgeslagen in interrupt pending bits (interrupt request vlaggen). Dit zal er ook voor zorgen dat gebeurtenissen die heel kort optreden niet verloren gaan. Het onthouden van de interrupt is ook nodig, omdat de CPU het nagaan van een interrupttoestand opschort bij het opstarten van een interruptroutine. Het is pas bij de uitvoering van de RETI instructie, dat
129 het nagaan van nieuwe interrupts opgestart wordt. Dit geeft problemen wanneer er bvb. twee interruptbronnen zijn, waarvan er eentje een lange interruptroutine heeft, en de ander met een klein interruptinterval optreedt. Tijdens de lange interruptroutine kan de snelle gebeurtenis zich voordoen. Door de pending bit kan de CPU na afloop van de andere interruptroutine de snelle nog detecteren. Komt die snelle twee keer tijdens de duur van de lange, dan kan de CPU het toch maar een keer waarnemen (er is immers maar een pending bit). Dit kan opgelost worden met de prioriteitsregeling. Het inschakelen/instellen van de interrupts gebeurt controleregisters. Op de volgende blz. zijn ze weergegeven.
via
drie
130
De watchdog interrupt wordt in het controleregister wdcon ingesteld. Deze interrupt gaat rechtstreeks naar de cpu, en kan dus niet uitgeschakeld worden met de ea bit in het ie SFR. 4.3 INTERRUPT PRIORITEITEN: In het systeem kunnen er verschillende interruptbronnen gelijktijdig actief zijn. Wat als er twee op hetzelfde moment een interrupt genereren? In onderstaande figuur is de standaard prioriteit opgenomen.
De prioriteit kan aangepast worden in de IP en IEIP2 registers (zie vorige paragraaf). Bij het gebruik van prioriteiten kunnen interruptbronnen in 1 van 2 niveau’s ondergebracht worden. De CPU scant de 2 niveau’s van hoog naar laag. Wordt een interruptbron ontdekt, dan worden van hetzelfde niveau, en van lagere niveau’s geen interrupts meer verwerkt. Pas na het uitvoeren van de reti instructie wordt het scannen op alle niveau’s hervat. Omdat tijdens de interruptroutine de hogere niveau’s nog altijd door de CPU gescanned worden, kunnen die interruptbronnen de
131 interruptroutines op de lagere niveau’s onderbreken. Merk op dat het aantal bytes tussen de vectoren gering is. Door in het programma LJMP instructies te plaatsen wordt de vector verschoven.
main:
sub1:
introut1: introut2:
org ljmp org ljmp org ljmp … ljmp … ret … reti … reti
msg1:
0000h ;start adres hoofdprogramma main 0003h ;start adres interruptroutine introut1 000bh ;start adres interruptroutine introut2 ;hier het hoofdprogramma main
;einde hoofdprogramma
;hier staan eventuele subroutines
;hier staat de eerste interruptroutine ;einde eertse intterruptroutine ;hier de tweede interruptroutine ;einde tweede interruptroutine db
…
;hier constanten
TOEPASSING Onderstaand programma laat op de pc een boodschap verschijnen, met de vraag om de tijd in te geven. Na de nodige initialisaties wordt op interrupt basis de tijd afgedrukt op het lcd scherm. org 0000h ljmp main
;hier start het hoofdprogramma ;springen over introut
org 0053h ;interrupt adres tic push acc ;registers bewaren push psw mov timecon,#01010011b ;tii bit clearen ; dit is een voorbeeld van een inetrrupt pending bit die door ; software gereset moet worden. mov a,#ff ;lcd scherm leeg maken lcall lcdoutchar mov a,hour ;hex uur waarde naar bcd
132 mov div lcall mov lcall mov lcall mov mov div lcall mov lcall mov lcall mov mov div lcall mov lcall pop pop reti
b,#0ah ab lcdoutnib a,b lcdoutnib a,#':' lcdoutchar a,min b,#0ah ab lcdoutnib a,b lcdoutnib a,#':' lcdoutchar a,sec b,#0ah ab lcdoutnib a,b lcdoutnib psw acc
;op lcd laten zien ;ascii code : op scherm ;zie uren
;zie uren
;registers herstellen ;einde introut
; na het uitvoeren van deze instructie worden interrupts terug ; toegelaten main:
lcall initlcd ;lcd in orde zetten mov a,#003h ;cursor uitschakelen lcall outcharlcd lcall initsio ;seriele poort initialiseren lcall ingave ;inlezen tijd lcall initrtc ;klok instellen ; de klok loopt nu op interrupt basis. Als hoofdprogramma hebben ; we een looplicht dat instelbaar is in snelheid en vorm. ; De commentaar is weggelaten. Zoek zelf eens uit wat het doet. loop: loop1: loop2:
loop3:
jnb mov sjmp mov clr movc jz jnb cpl mov
p0.1,loop1 dptr,#tabel1 loop2 dptr,#tabel2 a a,@a+dptr loop p0.0,loop3 a p2,a
133 inc dptr lcall delay sjmp loop2 tabel1:
db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db db
11111111b 11111111b 11111111b 01111111b 00111111b 00011111b 00001111b 00000111b 00000011b 00000001b 10000000b 11000000b 11100000b 11110000b 11111000b 11111100b 11111110b 11111111b 11111111b 11111111b 11111110b 11111100b 11111000b 11110000b 11100000b 11000000b 10000000b 00000001b 00000011b 00000111b 00001111b 00011111b 00111111b 01111111b 00000000b
tabel2:
db db db db db db db
01111111b 10111111b 11011111b 11101111b 11110111b 11111011b 11111101b
134 db db db db db db db db db db
11111110b 11111110b 11111101b 11111011b 11110111b 11101111b 11011111b 10111111b 01111111b 00000000b
; deze subroutine zet een boodschap op het scherm van de pc. ; de gebruiker moet dan de tijd ingeven. Er zijn geen controles ; voorzien. Bij foutieve ingave loopt het damn ook mis. ingave: ; in de aduc832 zit een bug. Die vangen we hier op: mov timecon,#001h ;dat was het dan mov lcall lcall mov Lcall mov mul mov mov lcall add mov mov lcall mov mul mov mov lcall add mov mov lcall mov mul mov mov lcall add
dptr,#boodschap siooutmsga sioinbufa r0,#strtbuf ascii1 b,#00ah ab b,a r0,#strtbuf+1 ascii1 a,b hour,a r0,#strtbuf+3 ascii1 b,#00ah ab b,a r0,#strtbuf+4 ascii1 a,b min,a r0,#strtbuf+6 ascii1 b,#00ah ab b,a r0,#strtbuf+7 ascii1 a,b
;boodschap naar scherm ;inlezen van ingave ;ascii naar hex omzetten
;laden tic register
135 mov sec,a ret boodschap: db formaat:',000h
cr,lf,'gelieve de tijd in te geven volgens het gekende
; De tic timer wordt ingesteld om elke seconde een interrupt te ; genereren. De tic registers werden reeds geladen in de ; routine ingave. initrtc:
mov intval,#001h ;alle hoeveel seconden een inetrrupt? mov timecon,#01010011b ;instellen dat we ; interrupts willen na een seconde, en dat de klok mag lopen setb ea ;inschakelen interrupts mov ieip2,#00000100b ;inschakelen tic interrupt ret ; deze routine geeft een tijdsvertraging die instelbaar is via ; p0. delay: delay1:
mov mov djnz djnz ret
a,p0 r2,a r2,delay1 r1,delay
LET OP!!!! Interruptroutines onderbreken het hoofdprogramma op een willekeurige plaats. In de interruptroutine worden registers gebruikt en soms ook vlaggen (psw). Om te beletten dat er data verloren gaat voor het hoofdprogramma kan het nodig zijn om de push en pop instructies te gebruiken. In sommige gevallen kan ook een andere registerbank gebruikt worden. Je kan dan in de interruptroutine verkort-adresseerbare registers gebruiken. Door de pop psw wordt de originele bank actief. Introut:
introut1:
push push setb … … add djnz … pop pop reti
acc psw rs0
;accu op stack ;vlaggen bewaren ;andere bank selecteren
a,#c9h r0,introut1
;gebruikt accu en vlaggen ;verkorte adressering
psw acc
;vlaggen herstellen+bank ;accu herstellen
136 4.5 REFERENTIES NAAR DE WEBSITE: Interrups komen aan bod in de 8051_programmers_guide.pdf in de webstek-8051\8051 general\ directory.
137
HOOFDSTUK 5: MICROCONTROLLER TOEPASSINGEN 5.1 INLEIDING: In de vorige hoofdstukken hebben we de ADuC832 controller besproken. Bij de realisatie van een project zal dat niet steeds een evidente keuze blijken. De controller is immers beperkt in I/O, zodat extra componenten noodzakelijk kunnen zijn. Een andere controller kan andere en of meer I/O bevatten.De C517 van Infineon is hier een voorbeeld van. Omdat het over een romless controller gaat zijn extra componenten en extern geheugen nodig. Een latch voor het demultiplexen van de adres/data bus, een rom chip, klok en reset componenten. De “GIANT” kaart is zo’n voorbeeld. De kaart wordt ook wel eens een “starter kit” genoemd. Een starter kit laat toe dat een gebruiker, zonder extra hardware, kleine programma’s kan uitproberen. De interface naar de toepassing is niet voorzien. Gezien het grote aantal mogelijke toepassingen is dat immers onmogelijk. In onderstaande figuur is het schema van een minimum C517 systeem opgenomen.
138 Deze controller werd recent vervangen door de XC888 van Infineon ( zie www.infineon.com ). Zowat alle fabricanten van controllers leveren 8051 compatibele devices (Atmel, Microchip, Silicon Laboratories, TI, …). Omdat de 8051-compatibelen zo populair zijn, is het aantal fabrikanten dat controllers met een 8051 core op de markt brengt gigantisch. Het aantal derivaten per fabrikant is groot. Waarom? Controllers worden gebruikt in low cost applicaties. Elke pin of mogelijkheid te veel, voert de prijs onnodig op. Gezien wij geen productie hebben is de zoektocht naar een minimale component ondergeschikt aan de problematiek van een bruikbare IDE en de beschikbaarheid van een programmer (noodzakelijk om het programma in de controller te krijgen). Meestal zijn er minimum order hoeveelheden, waardoor het bijna onmogelijk is om aan kleine aantallen controllers te komen. De productieduur van sommige controllers is bovendien zo kort, dat je ze maar eenmalig in een ontwerp kan gebruiken. Een stabiele controller (beschikbaar over een lange periode zoals de XC888 (sinds 2010)), waar je makkelijk aan kan geraken, is voor kleine aantallen een goede keuze. Dezelfde controller in verschillende applicaties gebruiken heeft als voordeel dat stock problemen achterwege blijven. Bovendien kan je de ervaring en software van vorige projecten recycleren. Een gekende controller gebruiken zal ook tijdswinst opleveren bij het schrijven van het programma. Via uw docent kan U meer info bekomen aangaande beschikbare controllers en borden op de campus. De gegevens van die borden (datasheets, schema’s, ondersteunende software,…) is terug te vinden op de website. In de volgende paragrafen verduidelijken we enkele standaard toepassingen. Dit zijn niet de enige manieren om de interfacing te verzorgen. Afhankelijk van de beschikbaarheid van interface componenten en doelstellingen van de I/O zal een andere schakeling gebruikt worden. 5.2 GALVANISCHE SCHEIDING: Bij het aansturen van een output, of het inlezen van een input, komt eerst de noodzaak van galvanische scheiding aan bod.
139
(b) opto thyristor (c) opto coupler (transistor) (d) opto coupler (darlington schakeling) (e) opto triac (solid state relais) De vier bovenstaande componenten kunnen voor een galvanische scheiding zorgen. Ze kunnen zowel voor inputals voor outputschakelingen gebruikt worden. Let wel, de galvanische scheiding wordt maar bekomen als de elektronica aan beide zijden aparte voedingen gebruikt (ook galvanisch gescheiden). Je kan deze optische componenten niet steeds rechtstreeks door de controller laten aansturen. Meestal is de diodestroom te groot en moet een extra buffer gebruikt worden. Opto componenten hebben het grote voordeel dat ze snel en onbeperkt kunnen schakelen. Een relais is een andere vorm van galvanische scheiding (zie onderstaande figuur). Omdat het een elektro-mechanische component is,
140 is de schakelsnelheid en de levensduur beperkt (1.000.000 schakelingen is gebruikelijk). Het is evident dat de karakteristieken van de gebruikte componenten zullen bepalen wat de scheidingsspanning is, en welke vermogens geschakeld kunnen worden. Merk ook op dat de printlayout onderworpen is aan veiligheidsrichtlijnen.
In bovenstaand voorbeeld wordt voor de spoel van het relais 24 volt gebruikt. Deze spanning is afhankelijk van de spoelspanning van het relais. Belangrijk in deze schakeling is het gebruik van een vrijloopdiode over de spoel van het relais.
5.3 AANSTUREN VAN EEN OUTPUT: Bij het aansturen van een output moet je altijd rekening houden met de spanning/stroom karakteristiek van de betreffende output pin. Dit is sterk controller afhankelijk. Bij de ADuC832 kan 1 TTL load gestuurd worden. Bij andere derivaten van de 8051 kan dat hoger liggen. Let op dat je ook de specifcaties per poort en per device nagaat. Zo zijn er controllers die 10mA per pin aankunnen, maar slechts 100mA per device. In onderstaande schema’s zijn enkele mogelijkheden weergegeven over hoe je de uitgansstroom kan opvoeren. De inverterende TTL buffer heeft wel voor gevolg dat na reset de LED in schakeling (a) licht zal geven. In schakeling (b) zal de LED dan weer uit zijn, en in schakeling (c) opnieuw licht geven. Je moet dus nagaan of de
141 interface schakeling ook bij reset van de controller een veilige toestand geeft (liefst alles off). Er bestaat een reeks van MOSFET circuits die de uitgnagsstroom aanzienlijk kunnen opvoeren. Dikwijls worden ook transistor array’s gebruikt. Van beide schakelingen staan er op de volgende blz. een ingekorte datasheet.
a)
c)
b)0
142
143
144
145 5.4 AANSTUREN VAN EEN INPUT: Bij het inlezen van een signaal moet je rekening houden met de configuratie van een 8051 input. Door de open drain structuur met inwenige pull-up volstaat het om een verbinding naar massa te maken wanneer je een 0 wil aanleggen. Bij het gebruik van een schakelaar treedt er contactdender op (zie onderstaande figuur)
De contactdender kan hardware (zie onderstaande figuren) of software opgevangen worden. In software wordt via een delay gewacht tot de dender voorbij is alvorens de schakelaar opnieuw te verwerken.
Figuur (a) maakt gebruik van een flip flop schakeling, figuur (b) gebruikt een schmitt trigger inverter. Om hardware uit te sparen wordt meestal gekozen voor een software ontdendering. De dendertijd is afhankelijk van de mechanische eigenschappen van de schakelaar.
146
; ; ; ;
dit programma wacht op het hoog worden van een schakelaar op p0.2. Telkens de schakelaar van laag naar hoog gaat, wordt een teller op het scherm van de PC met een verhoogd. de dendertijd van de schakelaar is 0.05s
switch
equ
p0.2
;schakelaar op p0.2
count
equ
20h
;register voor teller
org
0000h
;start adres programma
lcall lcall jnb lcall lcall
default clrscr switch,loop inctel prntel
;variabelen klaar zetten ;routine maakt scherm leeg ;wacht op schakelaar hoog ;teller aanpassen ;print teller op scherm
loop:
; is de contactdender kleiner is dan 2mS hoeft volgende wachtlus ; niet. Omdat we er van uitgaan dat de dender 0.05S is staat er ; een wachtlus
loop1:
lcall
del50ms
;wachten
jb lcall
switch,loop1 del50ms
;wacht op schakelaar laag ;ook hier dender mogelijk
ljmp
loop
;wachten op schakelaar terug hoog
; default geeft de variabelen een gekende startwaarde default:
mov ret
tel,#00h
;dit is het telregister
; clrscr maakt het scherm van de pc leeg. De routine gebruikt de ; accu. clrscr:
mov lcall ret
a,#ff siooutchar
;scherm eenmalig leeg maken ;naar scherm pc
; inctel zal de teller met een verhogen. Het tellen gebeurt ; decimaal. De routine gebruikt de accu en de vlaggen inctel:
mov add
a,tel a,#01h
;teller in de accu ;moet add anders geen da a
147 da mov
a tel,a
;corrigeren ;terug wegschrijven
; prntel drukt de waarde van de teller af op het scherm van de ; pc. Daarna wordt de cursor vooraan gezet. De routine gebruikt ; de accu en de vlaggen. prntel: mov a,tel ;waarde in de accu lcall siooutbyte ;naar het scherm van de pc mov a,#cr ;cursor vooraan zetten lcall siooutchar ret Onderstaande figuren geven twee mogelijkheden om een transducer met de controller te interfacen.
a)
b) Schakeling (a) laat toe om de 24 volt uitgang van de transducer om te zetten naar 5v. Schakeling (b) gebruikt een Schmitt trigger poort om te
148 beletten dat een trage overgang van 1>0 of 0>1 problemen geeft.
5.5 KEYBOARD INTERFACE: Dikwijls worden meerdere schakelaars gebruikt om gegevens in te lezen. Om klemmen te besparen worden die schakelaars in een matrix geplaatst (zie onderstaande figuur).
De colommen worden door de controller gelezen, de rijen geschreven. De pull-up weerstanden zijn eigenlijk overbodig (zijn reeds aanwezig in de controller). Ze worden toegevoegd als de draden naar het klavier langer worden dan 30cm. De waarde van de weerstanden mag niet kleiner zijn dan 3k8 (maximale Iout low van de controller is 1.3 mA, een gebruikelijke waarde is 4k7). In onderstaand programma wordt het klavier “bekeken” en in de accu een code geplaatst overeenkomstig de gedrukte toets. De gebruikte tabel is arbitrair opgesteld. Deze routine leest een ingedrukte toets maar 1 keer. We gaan er van uit dat het klavier aangesloten is op de klemmen van
149 poort 2. De rijen zijn aangesoten op de klemmen p2.0-p2.3 en de kolommen op p2.4-p2.7. Let op!!! Het gaat hier over een subroutine die opgeroepen wordt vanuit een hoofdprogramma. Hoofdprogramma: prevkey
equ
20h
org
0000h
;bijhouden vorige toets
; de scankey routine gebruikt dit register om te weten wat de ; vorige toets was. Bij het opstarten van het programma is er ; nog geen toets ingedrukt. Prevkey wordt op 0ffh gezet mov prevkey,#ffh
;niks ingedrukt
… ; keyscan leest het toetsenbord. Als de carry 1 is, is er een ; toets ingedrukt. De waarde wordt doorgegeven in de accu loop:
; ; ; ; ; ;
keyscan loop outbyte
;lees klavier ;wacht op toets ;waarde toets naar pc
keyscan leest het toetsenbord. Als bij deze scanbeurt een andere toets ingedrukt is dan bij de vorige, dan wordt de toetscode in de accu doorgegeven. Om aan te geven dat er een geldige code in de accu zit wordt de carry op 1 gezet. Staat de carry op 0 dan was er geen toets ingedrukt, of nog steeds de vorige. De routine gebruikt de accu, vlaggen en prevkey.
keyscan:
; ; ; ;
lcall jnc lcall …
push b push dpl push dph
;gebruiken we ook ;gebruiken we ook ;gebruiken we ook
Bij het lezen van het klavier gaan we elke rij om beurten met een 0 scannen. Telkens lezen we de kolommen om te zien of een schakelaar op het kruispunt met de betreffende rij gesloten is (zal laag lezen).
keyscan1:
mov b,#04h mov a,#11111110b push acc
;is lusteller ;gebruikt voor scannen ;accu effe bewaren
150 mov p2,a mov a,p2 orl a,#0fh
;scannen ;terug lezen ;laagste bits op 1
; als de accu 0ffh bevat was er geen toets ingedrukt cjne a,#ffh,keyscan2 ;toest ingedrukt ; de accu bevat 0ffh, er is dus geen toets ingedrukt. We gaan de ; volgende rij scannen pop acc rl a djnz b,keyscan1
;accu terug herstellen ;volgende rij nemen ;herhaal 4 keer
; als b=0 dan zijn alle rijen gescanned. Er is dan ook geen ; toets ingedrukt. Dit geven we aan door prevkey=0ffh te zetten ; en de carry =0 mov clr pop pop pop ret ; ; ; ; ; ;
prevkey,#ffh c dph dpl b
;registers herstellen ;einde keyscan
dit label wordt uitgevoerd als er een toets ingedrukt is b bevat een getal tussen 1-4, de accu xyzu1110b-xyzu0111b xyzu wil zeggen dat een van deze 4 bits op 0 zal staan. we gaan een nummer berekenen tussen 0 en 15 uit de waarden in die twee registers. Let op! Er zit nog een waarde te veel op de stack.
keyscan2:
dec
b
;b met een verminderen
; b bevat nu een getal tussen 0-3 (2 bits informatie)
keyscan3:
keyscan4:
push mov rlc jnc inc sjmp mov rl rl pop add
b b,#00h a keyscan4 b keyscan3 a,b a a b a,b
;effe bewaren ;we gaan b als teller=0 ;zoeken de 0 in xyzu ;gevonden ;verder zoeken ;herhaal ;b in accu ;bitjes opschuiven ;b herstellen ;a=0-15
151 mov dptr,#tabel ;startadres tabel laden movc a,@a+dptr ;vertalen ; we hebben de toets vertaald. We gaan na of het een nieuwe ; toets is door te vergelijken met de vorige gelezen waarde. cjne a,prevkey,keyscan5 ; er is geen nieuwe toets ingedrukt, we verlaten de routine clr c ljmp keyscan6 ; prevkey is niet gelijk aan de gelezen waarde, het betreft een ; nieuwe toets. Prevkey wordt aangepast. keyscan5:
mov prevkey,a setb c
keyscan6:
pop pop pop pop ret
b dph dpl b
;onthouden voor ;volgende keer ;aangeven geldige toets ;dummy pop ;registers herstellen ;einde keyscan
; de onderstaande tabel bevat de code die we aan een toets ; willen toekennen. De tabel is in dit voorbeeld opgevuld ; met willekeurige nummers tabel:
db db
000h,001h,002h,003h,004h,005h,006h,007h 0008h,009h,00ah,00bh,00ch,00dh,00eh,00fh
Er bestaan ook IC’s die een klavier scannen. De meeste programmeurs verkiezen software (nooit defect en ook geen leveringsproblemen, bovendien niet duur en kost ook geen printplaat).
5.6 DISPLAY MULTIPLEXING: Bij het aansturen van een reeks 7-segment uitlezingen heb je al vlug een hele reeks I/O klemmen nodig. Door het display te multiplexen kan je dat beperken. Bij het multiplexen zal elk display om beuren aktief worden (zie onderstaand schema). Vanaf 50Hz (elk display zal 50 keer per seconde licht geven) merk je niet dat er slecht 1 display licht geeft.
152
Als we ook het decimale punt willen gebruiken, komt er een segment lijn bij. Er zal bijkomende elektronica nodig zijn om voldoende stroom te kunnen leveren. Voor het aansturen van de uitlezing wordt best een interruptroutine gebruikt. De interruptfrequentie zal voor bovenstaand voorbeeld 50*4Hz zijn. Elke keer de interruptroutine opstart zal een volgend display aangestuurd worden. De interrupt routine gebruikt 4 (voorbeeld uit figuur) registers om de display info uit te lezen. Als voorbeeld geven we een interrupt routine die het scherm aanstuurd. Het voorbelldprogramma vorm slechts een onderdeel uit een groter geheel. disp0 disp1 disp2 disp3 div20 dispcount ; ; ; ; ; ; ; ; ; ; ;
equ equ equ equ equ equ
20h 21h 22h 23h 24h 25h
;display 0 data ;display 1 data ;display 2 data ;display 3 data ;deler voor interrupts ;teller aktief display
hier gaan we er van uit dat een timer 4000 interrupts per seconde zal genereren. Die delen we verder af naar 200 interrupts per seconde. de data die op het display moet komen staat in de dispx registers. introut is de interruptroutine we gaan er van uit dat de segmentlijnen op p6 zijn aangesloten een laag op p2 geeft dat de LED licht zal geven de selectielijnen zitten op p3.3-p3.2 (aktief laag), en worden door een 2 naar 4 decoder omgezet (onderdeel interface) dispcount is op 0 gezet door de initialisatie
153 Introut:
djnz mov push push clr clr push
div20,introute div20,#20d acc psw rs0 rs1 000h
;nagaan uitvoeren ;herladen teller ;hebben we nodig ;dit ook ;zeker zijn bank0 ;in gebruik ;=r0 op stack
; voor we iets aan het display veranderen eerst alles doven. ; als we dat niet doen krijgen we een nalicht effect. mov inc inc inc inc orl orl anl mov anl rr rr add mov mov pop introute:
pop reti
p2,#ffh ;doven display dispcount ;volgende display nemen dispcount ;tellen met 4 omhoog dispcount ;omdat we bits 2 en 3 dispcount ;gebruiken dispcount,#11110011b ;truc ? p3,#00001111b ;bits=1 p3,dispcount ;nieuw display aktief a,dispcount ;adres berekenen a,#0ch ;bits 2 en 3 nodig a ;bitjes op juiste a ;plaats a,#disp0 ;adres berekend r0,a ;pointer klaar p2,@r0 ;data naar display psw ;registers herstellen ;ook geselecteerde bank acc ;einde interruptroutine
5.7 STAPPENMOTOREN: Stappenmotoren worden vooral gebruikt voor open loop positionnering. Omdat een stappenmotor een vaste hoekverdraaiing geeft per aansturing, hoef je niet te meten hoe groot de resulterende verplaatsing is. Stappenmotoren hebben ook nadelen: relatief hoge kostprijs, laag maximaal toerental, relatief laag koppel. Er bestaan (b) unipolaire- en (a) bipolaire stappen-motoren. De onderstaande figuur geeft duidelijk het verschil tussen beide types motoren. Een bipolaire motor geeft, voor eenzelfde volume, een hoger koppel dan een unipolaire motor. Het nadeel is dat de elektronica om de motor aan te sturen complexer is.
154
Voor het aansturen van de motoren bestaan kant en klare componenten (zie onderstaande figuren).
155
Het aansturen van de motor herleidt zich tot het beurtelings aansturen van de wikkelingen. Onderstaande figuur laat dit duidelijk zien.
156
Figuur (a) is een afbeelding van een unipolaire motor. De serieweerstand wordt eventueel gebruikt om de stroom door de spoelen te meten. Figuur (b) geeft aan in welke volgorde de spoelen bekrachtigd moeten worden om de motor te laten draaien. De tijd tussen twee opeenvolgende patronen geeft de stapperiode. Diepgaande uitleg over het aansturen van stappenmotoren kan je terugvinden op www.st.com . In disk drives worden soms driefasige motoren gebruikt om de schijf met een synchrone snelheid te laten ronddraaien. Eigenlijk gaat het hier over driefasige stappenmotoren. BRUSHLESS motoren hebben geen bewikkelde rotor, in dit geval bestaat de rotor uit een permanente magneet. Onderstaande figuur geeft een schakeling met dit type motor.
157
5.8 DC MOTOREN: DC motoren kunnen hogere toerentallen halen, en leveren grotere koppels dan stappenmotoren. Bij regeling van positie en/of snelheid is een meetsysteem noodzakelijk. Vroeger werden analoge tacho’s gebruikt. Dit zijn kleine generatoren die een spanning/frequentie opwekken in functie van het toerental. Hoe langer hoe meer worden digitale tacho’s (ook wel encoders) gebruikt. Die geven een aantal pulsen per omwenteling. Er bestaan incrementele en absolute tacho’s. De absolute encoder geeft een bitpatroon afhankelijk van de positie van de rotor. Zie onderstaande figuur.
158
Dit type encoder bevat een “gewone binaire verdeling”. Normaal wordt de “Gray codering” gebruikt. De Gray codering voorkomt foutieve uitlezingen. Bij trage rotatiesnelheden kan de controller zelf de regeling op zich nemen. Soms worden hardware regelaars gebruikt. Die krijgen hun instelling (regelparameters) vanuit een controller. In onderstaande figuur wordt een regelsysteem weergegeven.
Merk op dat dezelfde vermogen componenten gebruikt worden als bij stappenmotoren. Om een DC motor in snelheid te regelen wordt een PWM sturing gebruikt. De 80c537 kan hardware matig zo’n signaal opwekken met timer 2 of de compare/capture unit. Niet alle controllers beschikken over een capcom of pca om dit te doen. Bij ontstentenis van een hardware PWM kan ook een software PWM gemaakt worden. Meestal gebruikt men hiervoor een timer interrupt.
159
5.9 LEVEL SHIFTERS VOOR SERIELE COMMUNICATIE: Zoals vroeger reeds besproken wordt er bij seriële communicatie gebruik gemaakt van level shifters. Die zetten een 0-5V signaal om naar een andere fysische grootheid (stroom, licht, frequentie, spanning,…). Hierdoor kunnen de signalemn grotere afstanden overbruggen. We beperken ons hier tot de twee level shifters die veel gebruikt worden. De belangrijkste is een RS232 omvormer. Onderstaande datasheet (deel) geeft de karakteristieken weer van de MAX232 die universeel gebruikt wordt. Het betreft hier een punt-punt verbinding met een spannings level shifter. Hiermee kan je minimaal 15m overbruggen (1m met TTL signalen).
160
In het geval dat: een busverbinding wenselijk is common mode spanningen onderdrukt moeten worden grote afstanden overbrugt moeten worden (1000m) wordt een RS485 interface gebruikt. Onderstaande blz geven wat meer info over een van de meest gebruikte componenten (75176 compatible).
De interface werkt diffirentieel. Het spanningsverschil tussen beide
161 communicatiedraden is belangrijk. Hierdoor worden common mode spanning onderdrukt. Er worden drie draden gebruikt voor de communicatie. De derde zal de massa van zender en ontvanger met elkaar verbinden. Bij hoge communicatiesnelheden moeten terminators gebruikt worden (2*120ohm). Om te beletten dat bij draadbreuk, of een tri state bus, foutieve data ingelezen wordt, is een fail safe maatregel nodig. Bij de 75176 worden weerstanden gebruikt die de ingangen van de omvormers op een gekend niveau houden. De MAX3082 uit de datasheets heeft die bescherming inwendig.
162
HOOFDSTUK 6: OEFENINGEN EN OPLOSSINGEN
6.1 INLEIDING EN WAARSCHUWING: In dit hoofdstuk komen een groot aantal oefeningen aan bod die ook in het labo opgelost moeten worden. Je kan ze eenvoudig overtypen. Zo zal je niets geleerd hebben. Gebruik dit hoofdstuk enkel om oplossingen te vergelijken. De oefeningen die hier staan runnen op fictieve hardware. Je zal het programma moeten aanpassingen voor de labo-opstelling. De oefeningen zijn minimaal voorzien van commentaar. Bovendien worden nogal wat trucs toegepast. Veel puzzelplezier!!! In de header van de voorbeelden staan vragen. Los ze op! In de oefeningen werden ook opzettelijk fouten gemaakt.
163 6.2 TE SNEL LOOPLICHT: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Dit programma is een veel te snel looplicht. ; Poort 2 wordt gebruikt om de LED's aan te sturen (aktief laag). ; De controller loopt op ongeveer 2Mhz. Hoeveel keer per seconde wordt ; het programma doorlopen? ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lus:
org Mov mov rr ljmp
0000h a,#11111110b p2,a a lus
;looplicht data ;laten zien ;info shiften
6.3 LOOPLICHT MET TIJDSVERTRAGING: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Dit programma is een looplicht met tijdsvertraging. ; Poort 2 wordt gebruikt om de LED's aan te sturen (aktief laag). ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lus:
lus1:
org mov mov rr mov mov djnz djnz jmp
0000h a,#11111110b p2,a a r0,#00h r1,#c2h r0,lus1 r1,lus1 lus
;looplicht data ;laten zien ;info shiften ;lusteller laden ;idem ;eerste lus ;tweede lus ;aanpassen info
; de lus wordt ? keer per seconde doorlopen. De tijdsvertraging met ; de twee lussen duurt ? micro seconden: ; ((12uS*256)+12uS)*194=598296uS
164 6.4 LOOPLICHT MET INSTELBARE TIJDSVERTRAGING: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Dit programma is een looplicht met instelbare tijdsvertraging. ; Poort 2 wordt gebruikt om de LED's aan te sturen (aktief laag). ; Poort 0 wordt gebruikt om 8 schakelaars in te lezen. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lus:
lus2: lus1:
; ; ; ; ;
org mov mov rr mov mov mov djnz djnz djnz ljmp
start a,#11111110b p2,a a r0,#00h r2,p0 r1,#003h r0,lus1 r1,lus1 r2,lus2 lus
;looplicht data ;laten zien ;info shiften ;lusteller laden ;schakelaars lezen ;lusteller laden ;eerste lus ;tweede lus ;derde lus ;aanpassen info
de lus wordt p3 keer per seconde doorlopen. De tijdsvertraging met de twee lussen duurt 8823 micro seconden: ((12uS*256)+12uS)*3=8823uS (een cycle duur 5.7 uS en niet 6uS) Door toevoeging van de derde lus wordt de tijdsvertraging: 8823*p3 en p3=3 t.e.m. 255=35292uS minimaal en 2.25S maximaal
6.5 LOOPLICHT VIA TABEL (LENGTE TELLEN): ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Dit programma is een looplicht met instelbare tijdsvertraging. ; Poort 2 wordt gebruikt om de LED's aan te sturen (aktief laag). ; Poort 0 wordt gebruikt om 8 schakelaars in te lezen. ; De informatie staat in een tabel. ; Deze uitvoering telt het aantal waarden in de tabel. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Start
equ
0000h
lus:
org mov mov clr
start r7,#004h dptr,#tabel a
;teller aantal bytes in tabel ;adres tabel in dptr
165 movc a,@a+dptr mov p2,a
;waarde uit tabel lezen ;laten zien
; tijdsvertraging
lus2: lus1:
tabel:
mov mov mov djnz djnz djnz inc djnz ljmp
r0,#00h r2,p0 r1,#003h r0,lus1 r1,lus1 r2,lus2 dptr r7,lus start
;lusteller laden ;schakelaars lezen ;lusteller laden ;eerste lus ;tweede lus ;derde lus ;pointer aanpassen ;4 keer uitvoeren ;aanpassen info
db db db db
11100111b 11011011b 10111101b 01111110b
;LED waarden
6.6 LOOPLICHT VIA TABEL (VERBODEN CODE): ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Dit programma looplicht met instelbare tijdsvertraging. ; Poort 2 wordt gebruikt om de LED's aan te sturen (aktief laag). ; Poort 0 wordt gebruikt om 6 schakelaars in te lezen. ; De informatie staat in een tabel. ; Deze uitvoering gebruikt een verboden code. Dit is een waarde die in ; de tabel niet voorkomt, tenzij op het einde. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Verboden
equ
10101010b
;deze waarde mag niet voorkomen ;in de tabel (behalve laatst)
Start
equ
0000h
;start adres hoofdprogramma
org mov clr movc cjne ljmp
start dptr,#tabel ;adres tabel in dptr a a,@a+dptr ;waarde uit tabel lezen a,#verboden,lus3 ;testen op verbodebn code start
lus:
166 lus3:
mov p2,a
;laten zien
; tijdsvertraging
lus2: lus1:
tabel:
mov mov mov djnz djnz djnz inc ljmp
r0,#00h r2,p0 r1,#03h r0,lus1 r1,lus1 r2,lus2 dptr lus
;lusteller laden ;schakelaars lezen ;lusteller laden ;eerste lus ;tweede lus ;lus sluiten ;pointer aanpassen ;aanpassen info
db db db db db
11100111b 11011011b 10111101b 01111110b verboden
;LED waarden
;geeft einde tabel aan
6.7 16 BIT HEX TELLER OP SCHERM PC: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; dit programma laat een 16 bit hex teller op het scherm van de PC zien. ; de teller gaat tegen volle snelheid. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Start
equ
00000h
;start adres hoofdprogramma
; de teller zit in twee registers Tell Telh
lus:
equ equ
20h 21h
;register met 8 lage bits ;idem 8 hoge bits
Org
start
;hupla vertrekken
mov lcall mov mov
a,#ff siooutchar tell,#00h telh,#00h
;scherm eenmalig leeg maken ;ascii code naar scherm sturen ;teller met 0 laden ;16 bit groot
mov lcall mov lcall
a,telh siooutbyte a,tell siooutbyte
;afdrukken 16 bit getal ;in twee keer
167 mov a,#cr mov add mov mov addc mov ljmp
a,tell a,#01h tell,a a,telh a,#00h telh,a lus
;cursor vooraan lijn ;volgende keer overschrijven ;getal met 1 verhogen ;moet zo voor carry ;terug wegstoppen ;eventuele carry bij tellen ;terug wegstoppen ;herhaal
168
Litteratuurlijst Deze cursus werd samengesteld met informatie die vrij beschikbaar is op het WWW. We verwijzen naar volgende websites als belangrijkste bronnen: www.analog.com www.infineon.com www.intel.com www.google.be My brain (;-))