PIC microcontroller tutorial

Gepost door Bastiaan Steenbergen op donderdag 4 december 2003 00:33

Inleiding

Bij deze microcontroller tutorial willen we je leren hoe je een microcontroller kunt programmeren en gebruiken. We hebben hiervoor een stuk theorie geschreven om je de basisbeginselen van de microcontrollers te leren. Daarnaast hebben we een praktijk gedeelte wat is opgebouwd rond een project, zodat je zelf thuis kunt experimenteren met een echte microcontroller.

Er is gekozen voor een microcontroller van het merk Microchip van het type PIC16F84A. De reden voor deze keus is omdat het een redelijk simpele maar toch veelzijdige controller is. Makkelijk te leren en je kunt er een hoop mee.

We hopen dat je er veel van zult leren en dat je veel plezier zult gaan beleven met het gebruik van microcontrollers. Er gaat een wereld voor je open zodra je er mee kennis hebt gemaakt.

Wat is een microcontroller

Een microcontroller is een digitaal IC dat volledig programmeerbaar is en taken kan verrichten. Het bevat een klein geheugen waarin je je eigen data kunt programmeren. De chip zal, als die wordt ingeschakeld, het door jouw geschreven programma uitvoeren. In het vervolg zal het woord microcontroller worden afgekort als C.

Wie maken ze

Er zijn verschillende fabrikanten die deze C's maken, om er een paar te noemen: Motorola, Atmel en Microchip. Maar er zijn er nog veel meer. Wij gebruiken er eentje van Microchip. Zodra je redelijk bekent bent met deze C dan zul je weinig moeite ondervinden bij het leren van een andere, aangezien de techniek en werking van de meeste types op elkaar lijkt.

Welke types zijn er

Er bestaan veel verschillende types. Elk met hun eigen karakteristieken en functionaliteiten. Zo zijn er die alleen normale I/O poorten hebben, maar zijn er ook die speciale, zoals A/D converters of RS232 poorten, bevatten. Veel types kun je onderbrengen in een groep omdat ze dezelfde functies bevatten, deze groep noemen ze dan een familie. Elke chip in zo'n familie heeft dan bepaalde eigenschappen gemeen met de rest. Het verschil zit hem dan in de extra functies/opties die sommige dan weer bevatten. Zo kan binnen een groep alleen het aantal RAM geheugen dat beschikbaar is bijvoorbeeld verschillen.

Voordelen

Er zijn veel voordelen om een C te gaan gebruiken. Zo kun je volledig zelf bepalen wat een stukje elektronica moet doen, en in welke volgorde. Wil je later de uitvoering wijzigen dan hoef je alleen de chip eruit te halen, opnieuw te programmeren, en er weer in te plaatsten. Ook het testen is zeer gemakkelijk. Een ander voordeel is dat je veel functionaliteit krijgt en er weinig ruimte voor hoeft in te leveren op je printplaat. Het IC wat wij gebruiken zit in een DIP behuizing van 18 pinnen. Er is zelfs nog een SMD versie te koop die al helemaal weinig ruimte inneemt.

Nadelen

Er zijn veel voordelen bij C's maar ook nadelen. Zo moet je redelijk wat kennis hebben van digitale techniek. Zowel op hard- als software gebied. Ook moet je moet beschikken over een stuk hardware waarmee je de C kunt programmeren, oftewel waarmee je het geschreven stuk software in het IC laad. Op zich niet zo'n probleem maar de benodigde officile hardware van de fabrikant kost zeer veel geld. Voor de hobbyist zou dit een belemmering kunnen vormen. Toch is dit maar een relatief klein nadeel omdat er op internet schema's van programmeerhardware zijn te vinden waarmee je ook je controller kunt programmeren. Ook de compilers en simulators die je nodig hebt kunnen voor redelijk wat kosten leiden. Toch zijn deze ook wel gratis op het internet te vinden.

Hardware

De microcontroller die we gaan gebruiken is een geavanceerd stukje hardware, en behoort met externe componenten te worden ingesteld. Hieronder zullen we de diverse belangrijke hardware bespreken die de PIC nodig heeft. Ook zullen we de interne hardware bespreken die aanwezig is in de PIC. Om zelf ook makkelijk straks je weg te vinden bij andere merken of types van C's is het aan te raden de datasheet erbij te pakken en tegelijk dingen op te zoeken als ik die vermeld. Hier kun je die downloaden.

Voeding

Om te beginnen de voeding. De C heeft een gelijkspanning nodig tussen de 2v en de 5,5v. Het is het meest handigst om te kiezen voor een voedingsspanning van 5v. Aangezien de eventuele digitale ic's die je eraan wilt koppelen ook op die spanning werken en omdat het niveau van de logische 1 een 5 volt signaal is. Het enigste wat er moet gebeuren is op de pin 5 de ground aan te sluiten en pin 14 de voedingsspanning.

Heb je geen gestabiliseerde 5v dan kun je dit schema gebruiken om een spanning tussen de 18v en 7v om te zetten naar een mooie 5v voor de voeding.

Stabiele voedingStabiele voeding

Klok

Een C loopt net zoals je pc op een bepaalde frequentie. Dit heet de klokfrequentie. Alle bewerkingen van de C lopen op dat signaal. Om het juiste clocksignaal aan te bieden kun je een oscillator aansluiten zoals te zien is in de onderstaande afbeelding. Intern wordt de frequentie van de clk door 4 gedeelt en de uitkomst daarvan is de snelheid waarmee instructies worden uitgevoerd. De reden hiervoor is dat de interne processor een instructie eerst moet ophalen en decoderen voordat deze het daadwerkelijk kan gaan uitvoeren. Zou je dus een externe clock van 4MHz aansluiten dan worden de instructies uitgevoerd met 1MHz, en dat is dus 1 instructie per 1s. Hieronder zie je hoe een externe clock (kristal) wordt aangesloten.

Aansluiting clockAansluiting clock

Ook heb je de mogelijkheid de clock in de vorm van een RC-schakeling aan te bieden. In de datasheet kun je meer informatie vinden over de waarde van de condensatoren, en hoe het zit met de waarde van het kristal.

Reset

Een C heeft de mogelijkheid om te worden gereset via de MCLR pin. Dit is vooral handig als de controller niet meer goed reageert of volledig is gestopt met zijn werkzaamheden. Door te resetten komt de C in zijn beginstand en begint weer het geprogrammeerde programma vanaf het begin uit te voeren. Ook worden alle instellingen van de controller weer in de default waarde gezet. De pin MCLR werkt net iets anders dan je misschien bent gewend. Het is namelijk een 'Active low' pin. Dat betekent dat je de chip reset als je een 0 op de pin zet (bij de meeste normale digitale logica gebeurt er iets zodra je er een 1 op zet). Wil je dat de controller normaal zijn werk doet dan moet je dus zorgen dat die pin altijd een 1 krijgt. In de datasheet is een 'Active low' pin aangegeven met een lijn boven de naam van de pin. Hieronder zie je een schema met daarin hoe je de MCLR moet aansluiten om de chip gewoon zijn werk te kunnen laten uitvoeren.

ResetReset

Er wordt een 1 op die pin gezet door de voeding er gewoonweg op te zetten. Wel is er een weerstand van 10k tussen gezet om de stroom die de chip binnen gaat te beperken. Je kunt ook nog kiezen om een schakelaar toe te voegen zodat je de C makkelijk kunt resetten. Door middel van deze schakelaar wordt de reset pin laaggetrokken naar GND, en zal de controller dus in de reset komen. Zie onderstaande tekening.

Reset met knopReset met knop

Instructies

Zonder software doet de C natuurlijk niets. Er moet een stukje software worden geschreven dat vervolgens naar de chip kan worden overgebracht. De chip bevat daarvoor een stuk Flash geheugen dat volgens de fabrikant zo'n 10.000x kan worden gelezen/geschreven. In dit geheugen komt je programma te staan. Zodra de voeding eraf is wordt dit geheugen niet gewist en blijft je programma bewaart. Er is ruimte aanwezig voor 1024 regels code.
Het schrijven van het programma gebeurt op de computer door middel van een taal genaamd 'assembler'. Deze taal werkt door middel van commando's die we 'instructies' noemen in het microprocessor jargon. Deze C bevat een RISC processor wat inhoudt dat deze weinig instructies kent. Zeer gemakkelijk dus voor de beginner, omdat deze maar weinig instructies hoeft te kennen. Er zijn er in totaal 35 verschillende, met elk hun eigen functionaliteit. In het hoofdstuk "Software" gaan we verder in op het schrijven van een applicatie door middel van instructies.

Registers/Geheugen

Als je kijkt naar de datasheet op pag. 3 Fig 1-1 dan zie je hoe de chip intern is georganiseerd. In het midden is een blokje ALU te vinden. Dit staat voor Arithmetic Logic Unit en is het eigenlijke rekenwonder in de C. Deze ALU kan berekeningen uitvoeren. Om dat te doen moet er data naar deze unit worden getransporteerd. Dit gebeurt door gebruik te maken van een stuk geheugen dat is gekoppeld aan de ALU. Deze geheugens heten registers. Deze controller heeft maar n register van 8 bits breed, wat de fabrikant het werkregister noemt. Het heeft de afkorting "W" gekregen. Microcontrollers van een ander type of fabrikant kunnen er meer hebben, en soms ook nog eens een andere breedte hebben. Om een voorbeeld te geven: de Motorola 68000 heeft wel 16 registers van 32 bits breed maar dat is ook een heel wat complexere dan onze PIC. Het W register dienen we te gebruiken bij de verplaatsing van data naar de ALU en weer terug, en voor de verplaatsing tussen data-geheugens onderling.
De ALU voert de berekeningen uit maar dat is niet genoeg. Die data moet wel kunnen worden opgeslagen anders heb je er weinig aan. Zo is er op de chip 68 bytes data RAM aanwezig om onder andere waardes van variabelen en constanten in te kunnen bewaren. Deze C heeft ook nog 11 RAM locaties die niet voor de gebruiker vrij zijn te gebruiken. Het zijn de SFR (Special Function Registers). Deze worden gebruikt om instellen van bv poorten en interrupts in op de slaan. Zie de datasheet pagina 7 voor deze speciale registers.
Hieronder zie je een "plattegrond" van het RAM geheugen. Hier kun je zien dat de nummering compleet hexadecimaal gaat. Door de h achter het getal geef je duidelijk aan dat het om een hex waarde gaat.

Indeling RAMIndeling RAM

Je kunt hier zien dat het gebied is opgesplitst in verschillende delen. Het geheugen bestaat uit 2 banken. Bank 0 en Bank 1. Nu zit het geheugen dat vrij te gebruiken is op adres 0Ch tot 4Fh, dit is 68bytes. De 1e 11 zijn voor de speciale instellingen gereserveerd, en kun je dus niet zelf zomaar als geheugenplek gebruiken, omdat ze een speciaal doen hebben gekregen.

I/O en Poorten

Een C heeft verschillende in- en uitgangen. Je kunt deze gebruiken als I/O. Zo kun je er van alles op aansluiten om er wat mee te besturen of te meten. Meestal zijn er een reeks pinnen die bij elkaar horen. Deze reeks of verzameling van pinnen heten een "poort". Zo heeft onze C twee poorten. Poort A en poort B. Poort A bestaat uit 5 pinnen, en poort B bevat er 8. Als we kijken naar de datasheet (pag. 1) dan zien we dat Poort A zit op de pinnen, 1, 2, 3, 17 en 18. De pinnen van poort B zitten op de pinnummers 6, 7, 8, 9, 10, 11, 12, 13.

PinoutPinout

Het is natuurlijk allemaal digitaal, dus kan een pin maar 2 toestanden hebben, een 1 of een 0. Voordat je de poorten kunt gebruiken moet je ze eerst instellen, namelijk of het in- of uitgangen zijn. Bijna elke pin kun je apart in- of uitgang maken. Je geeft dat aan bij de C door in een stukje geheugen bits neer te zetten. Elke plek in dat geheugen vertegenwoordigt een I/O pin en daar kun je een 1 of 0 inzetten. Hieronder staat een stukje uit de datasheet.

Instellen van in- en uitgangenInstellen van in- en uitgangen

Hier zie je dat op adres 85h het "data direction register" zit van poort A. Omdat poort A maar 5 pinnen heeft kun je alleen bit 0 t/m bit 4 instellen. De rest is toch niet van belang.
Een 0 betekent dat de pin een uitgang is. Als je er een 1 in zet dan stel je die pin in als ingang. In de kolom "Value on Power-on RESET" kun je vinden wat de waardes zijn bij de default waarde na een reset. Zo zie je dat alle I/O pinnen ingangen zijn zodra de C wordt gereset.

Om het nog even extra toe te lichten een klein voorbeeld.
Stel: je wilt dat van poort B de pinnen RB0, RB1, RB4 en RB7 uitgangen zijn en de rest ingangen. Je moet er dus met een commando voor zorgen dat de binaire waarde 01101100 op adres 86h komt te staan. Zo staan op de plekken die die pinnen vertegenwoordigen 0'en zodat ze uitgangen zijn, en 1'en op de plekken van de pinnen die ingangen dienen te zijn.

Het schrijven van een programma

Zonder software zal een C weinig uithalen. Deze software moet eerst zelf worden ontworden en geschreven. Het schrijven van software gebeurt net zoals dat bijvoorbeeld in C++ programma's worden geschreven. Alleen zijn de commando's wat anders. C++ is een hogere programmeertaal. De taal waarmee je een C programmeert is een lagere taal. Deze taal noemen ze assembler taal. Waarom het een lagere taal heet wordt je tijdens de uitleg van de taal wel duidelijk. Zodra de software klaar is moet het worden geassembleerd. Dit wordt gedaan door een assembler die afhankelijk is van het type en merk van de C. Elk merk fabrikant heeft zijn eigen assembler. Een assembler zet het geschreven programma om in een string van hexadecimale tekens die de C begrijpt.

Omdat een C nu eenmaal digitaal werkt zul je heel veel bij het programmeren tegen bits en en bytes oplopen. Ik veronderstel dat je wel weet wat bits zijn en hoe de nummering werkt en wat de plaatswaardes zijn van bits. Microcontrollers kunnen alleen hele simpele bewerkingen uitvoeren. Toch kun je, met al die bewerkingen samen, een heel groot complex geheel maken. Het rekenen gaat met bits, het verplaatsen gaat met bits, alles gaat gewoon met bits.

Om deze tutorial wat leuker en meer praktischer te maken hebben we er een miniproject aan vastgeknoopt. We zullen voor dat project een programma schrijven dat uiteindelijk hardware zal aansturen. Ondanks dat de C weinig commando's heeft bespreken we ze niet allemaal. De reden darvoor is dat veel commando's op elkaar lijken zodoende kun je ze zelf ontdekken.

Commando's heten in assembler taal "Instructies". De C die we gebruiken heeft 35 van deze instructies. Hij valt daarom onder de categorie RISC processor. Dit betekent "Reduced Instruction Set Computer", wat inhoudt dat hij maar weinig instructies heeft. Er bestaan processors met veel meer instructies. Alle instructies worden sequentieel uitgevoerd, dus in de volgorde dat ze zijn geschreven. We zullen uitleggen hoe het uitvoeren van instructies werkt, zodat je een beter beeld hebt van het geheel en beter snapt waarom je iets op een bepaalde manier moet doen en niet anders. De chip heeft een permanent programmageheugen. Hierin staan jouw eigen instructies onder elkaar. Zodra de C een voedings spanning krijgt aangeboden, en er een kloksignaal aanwezig is op zijn klok-input-pinnen, gaat die beginnen. Intern heeft de controller een teller die bijhoudt waar die is met het uitvoeren van zijn instructies. Deze teller noemt men "program counter". Helemaal in het begin is deze program counter 0000, wat dus inhoudt dat die de instructie op adresgeheugen nummer 0000 moet gaan uitvoeren. Deze counter wijst altijd naar de volgende uit te voeren instructie. Omdat het wijst naar 0000 wordt dit geheugenadres gelezen en wordt de instructie, zoals ze dat noemen, opgehaald. En wordt meteen de program counter verhoogd met 1 zodat die weer wijst naar de volgende uit te voeren instructie. Na het ophalen van een instructie wordt deze vertaald zodat de processor weet wat die ermee moet gaan doen. Zodra die gereed is met het uitvoeren van zijn instructie kan die zijn volgende instructie gaan ophalen, vertalen en uitvoeren, meer gebeurt er niet. Het is telkens ophalen, vertalen en uitvoeren van instructies. Dit gaat net zolang door tot het programma is afgelopen of totdat er een externe reset word gegeven.

Zoals er bij het "Hardware" gedeelte is vertelt werkt deze controller met een working register, W genaamd. Dit register wordt gebruikt voor het transport van data, namelijk van dataregister tot dataregister. Er kunnen alleen complete bytes worden verplaatst, dus een reeks van 8 bits. Wel is het mogelijk om direct een bit in het geheugen te veranderen van 0 naar 1 of andersom. Zodra er een 1 staat en je wilt er een 0 van maken dan heet deze handeling het "clearen" van dat bit. Wil je van een 0 een 1 maken dan noemen ze dit het "setten" van een bit.
Stel dat je de data uit register 0Ah naar register 21h wilt verplaatsen dan dien je eerst de data uit 0Ah naar het W register te verplaatsen, en dan kan het W register het pas verplaatsen naar dataregister 21h. De adressen en getallen die worden gebruikt bij de assember taal zijn meestal hexadecimale getallen. Je dient dit aan te geven in je programma. Zo is 21h de hexadecimale variant van het decimale 33d. Die h geeft dus aan dat het om een hex-getal gaat en de d dat het om een decimaal getal gaat. Alle niet ingevulde bits voor je getal worden als een 0 verondersteld. Zet je bijvoorbeeld in de assembler 21h neer dan is dit eigenlijk 021h. Je kunt als je dat wilt ook een hexadecimaal getal neerzeten in de vorm van 0x021. In C is dit een veelgebruikte methode om hexgetallen weer te geven. Wil je nu een waarde boven 9Fh neerzetten dan dien je dit vooraf te laten gaan door een 0.
Dus A0h of hoger kent die niet en zal een error geven. Je moet dan neerzetten 0A0h. Bij de andere notatie 0x0A0 heb je hier uiteraard geen last van.

Hieronder zie je een lijst met alle instructies die er zijn:

Instructie Parameters Betekenis
ADDLW k Add literal and W
ADDWF f, d Add W and f
ANDLW k AND literal with W
ANDWF f, d AND W with f
BCF f, b Bit clear f
BSF f, b Bit set f
BCF f, b Bit clear f
BTFSC f, b Bit test f, Skip if clear
BTFSS f, b Bit test f, Skip if set
CALL k Call subroutine
CLRF f Clear f
CLRW - Clear W
CLRWDT - Clear Watchdog Timer
COMF f, d Complement f
DECF f, d Decrement f
DECFSZ f, d Decrement f, Skip if 0
GOTO k Go to address
INCF f, d Increment f
INCFSZ f, d Increment f, Skip if 0
IORLW k Inclusive OR literal with W
IORWF f, d Inclusive OR W with f
MOVF f, d Move f
MOVLW k Move literal to W
MOVWF f Move W to f
NOP - No operation
RETFIE - Return from interrupt
RETLW k Return with literal in W
RETURN - Return from subroutine
RLF f, d Rotate Left f through Carry
RRF f, d Rotate Right through Carry
SLEEP - Go into standby mode
SUBLW k Subtract W from literal
SUBWF f, d Subtract W from f
SWAPF f, d Swap nibbles in f
XORLW k Exclusive OR literal with W
XORWF f, d Exclusive OR W with f

Links staan de instructies, daarnaast staat wat je voor extra info bij elke instructie moet bijvoegen. En helemaal rechts zie je samengevat wat ze doen. Een literal is een getal.
De betekenis van de letters:

  • f staat voor registerfile, van 00h tot 4Fh
  • d voor destination select. 0 = sla het resultaat op in W, 1=sla het resultaat op in de file
  • b voor bit address
  • k voor constante data of een label
  • - is niks
  • W is working register

We zullen de werking van vele instructies toelichten. Je zult er misschien niet meteen veel van begrijpen, maar dat is niet erg. Als je er mee aan de slag gaat kun je hier naar terugkijken. Het is nu alleen even belangrijk dat je weet wat er ongeveer is en wat het doet. We zullen beginnen bij de bit manipulaties.

BCF f, b Bit clear f

Dit is een instructie waarmee je een bit kunt clearen, dus een 0 maken. Je moet als extra info opgeven in welke file het bit staat, en om welk bitnummer het gaat.
Voorbeeld: BCF 25h, 4h
Hierbij zal dus bitnummer 4 in datafile op locatie 25h, een 0 worden. Stel dat er in adres 25h de bitreeks 0111.0111 staat, dan zal na deze instructie de data op dat adres er als volgt uitzien: 0110.0111

BSF f, b Bit set f

Dit is dezelfde instructie als hierboven alleen dan wordt het bit op de gegevens locatie geset.

MOVLW k Move literal to W

Hiermee kun je in het W-register een getal plaatsen.
Voorbeeld: MOVLW A4h
Het W register zal dan na deze instructie de waarde A4h bevatten.

MOVWF f Move W to f

Hiermee kun je de waarde van W in een file zetten.
Voorbeeld: MOVWF 25h
Stel dat er in W de waarde A4h stond dan bevat na deze instructie de file op adres 25h ook deze waarde. De waarde die in W staat blijft er ook instaan. De MOV die in de instructie staat is wel wat verwarrend, want dit zou suggereren dat het wordt verplaatst. Maar in werkelijkheid wordt het alleen maar gekopieerd.

MOVF f, d Move f

Bij deze instructie wordt de data uit een file gekopieerd naar het W-register.
Voorbeeld: MOVF 25h, 0
Dit zorgt ervoor dat de data uit file 25h in het register W wordt geladen.
Voorbeeld: MOVF 25h, 1
Dit zorg ervoor dat de data niet wordt verplaatst. Wat heeft dat nu voor nut zou je zeggen. Nou toch kan het handig zijn. Het zit namelijk zo. Intern bevat de chip een ALU zoals we hebben toegelicht in het "Hardware" gedeelte. Deze ALU voert alle berekeningen uit. Na een berekening levert de ALU de uitkomst, maar ook geeft deze door middel van een "statusregister" extra informatie. Het statusregister is een dataregister dat zit op adres 03h en het ziet er als volgt uit:

Status registerStatus register

Nu gaat het ons alleen even om het Z-bit en het C-bit. Zoals je kunt zien zit het Z-bit op bitnummer 2, en het C-bit op nummer 0. De Z staat voor Zero. Zodra er een berekening/bewerking wordt uitgevoerd en de uitkomst is een 0, dan maakt de processor van bitnummer 2 een 1'tje. Zo kun je door na die berekening te kijken naar dat bit zien of de uitkomst een 0 was of niet. Het C-bit staat voor Carry. Dit is wel bekend uit de digitale wereld denk ik. Als er een berekening wordt uitgevoerd die een uitkomst heeft die groter is dan de 8bits die ter beschikking staan dan geeft dit bit dat aan. Dus zou je de binaire waarde 1000.0000 optellen bij 1000.0000 dan wordt de uitkomst 1.0000.0000. Maar aangezien de processor maar met 8 bits werkt blijft er als resultaat 0000.0000 over. En om dan dus aan te geven dat het allerlinkse bit buiten het bereik viel maar er wel was wordt het allereerste bit (dus bitnummer 0) van het statusregister een 1. Ook wordt dit bit gebruikt bij de omgekeerde bewerking. Dus bij aftrekkingen die een Borrow nodig hebben. In de datasheet kun je vinden welke instructie welk bit in het statusregister benvloed.

Om weer terug te komen bij de instructie MOVF 25h, 1. Deze bewerking laat het getal gewoon in de file zitten, maar werkt wel het statusregister bij. Zo kun je dus informatie krijgen over de waarde van de data in dat register.

CLRF f Clear f
CLRW - Clear W

Deze instructies lijken erg op elkaar en daarom behandel ik ze samen. Je kunt hiermee het register clearen. Dat wil zeggen van alle bits 0'len maken.
Omdat er maar 1 W-register is is het niet nodig extra info bij de instructie op te geven. Bij het clearen van een file moet de controller echter wel weten welke file het is.
Voorbeeld: CLRF 25h
Zo wordt de data in het dataregister op adres 25h binair gezien 0000.0000.

SWAPF f, d Swap nibbles in f

Een file bevat 1 byte aan data, dat zijn dus 8 bits. Nu noemen ze in de digitale wereld de linker en rechterhelft nibbles. Zou de file er als volgt uitzien in bits: 1010.1111
Dan is het linker nibble 1010 en het rechter nibble 1111. Met de bovenstaande instructie kun je die 2 helften met elkaar laten omwisselen, daarom heet het swap.
Zou je dit dus uitvoeren op het voorbeeld 1010.1111 dan zal de data erna 1111.1010 zijn.

COMF f, d Complement f

Met deze instructie is de data te complementeren, wat wil zeggen inverteren. Dus alle bits omdraaien van 0 naar 1 en andersom. Je behoort mee te geven om welke file het gaat en of het resultaat in de file weer moet worden opgeslagen of dat het in W moet worden geplaatst.
Voorbeeld: COMF 25h, 0
Met deze instructie worden de bits in file 25h gecomplementeerd en wordt de uitkomst in W geplaatst. Stel dat er 1000.1111 in de file staat. Na deze instructie staat er dan in het W-register 0111.0000. Let op!, de data in die file blijft onveranderd, het antwoordt werd namelijk niet weer in die file opgeslagen.
Die instructie zorgt ervoor dat de data uit file 25h in de ALU wordt gedaan. De ALU complementeert deze bits. Dus aan de data in de file is niks verandert. En door de optie 0 in de instructie wordt het resultaat in het W-register geplaatst zonder nog aan de file te zitten.

NOP - No operation

Een van de eenvoudigste instructies. Deze doet namelijk helemaal niks. Wat is dan het nut zou je zeggen. Je kunt zo een vertraging in je programma zetten. Stel dat je een extern geheugen vraagt om data. Dit is bijvoorbeeld niet direct beschikbaar. Door een of meerdere NOP's te plaatsen kun je de processor even in tijd laten wachten. Stopzetten kan namelijk niet, immers de klok blijft doorlopen, en daar loopt de C op.

DECF f, d Decrement f
INCF f, d Increment f

Deze twee instructies nemen we samen aangezien ze simpel zijn en vrijwel identiek qua toepassing. Decrement staat voor vermindering. Deze vermindering houdt in dat de data die in de file staat met n omlaag gaat. De andere instructie doet het omgekeerde, deze zal de waarde van de inhoudt van de file met n ophogen. Het resultaat wordt afhankelijk van de gekozen waarde voor d in W of in de file geplaatst.

ADDLW k Add literal and W

Een instructie die je zo nu en dan eens nodig zult hebben. Je kunt hiermee getallen optellen bij de inhoud van W.
Voorbeeld: ADDLW 04h
Dus stel dat je 04h wilt optellen bij de waarde 24h, dan moet je zorgen dat bv. de 24h in W zit en dan deze instructie uitvoeren met als parameter 04h. Na deze instructie zal W het antwoordt bevatten en dat is in ons voorbeeld dus 28h.

ADDWF f, d Add W and f

Deze functie is vergelijkbaar met die van hierboven. Het enigste verschil is dat nu W wordt opgeteld met de data die in een file zit.
Je kunt door voor d een 0 te maken het resultaat in W laten zetten. De file zal dan alleen worden gebruikt om de data te lezen en zal niet veranderen. Kies je echter voor d de waarde 1, dan is het weer net andersom. Dan zal het antwoordt in de file worden geplaatst en zal W niet wijzigen.

SUBLW k Subtract W from literal

Dit is een instructie waar je snel de mist in kan gaan. Hiermee kun je namelijk W van een getal aftrekken, dus dat is de volgende bewerking: k - W = W. Let dus op, het is dus NIET W - k = W.
Voorbeeld: SUBLW 26h
Stel nu dat er 14h in W staat dan wordt het antwoordt van deze som: 26h - 14h = 12h. Het antwoord wordt altijd in W geplaatst.

SUBWF f, d Subtract W from f

Deze instructie is weer wat meer logisch dan de vorige. Hiermee wordt namelijk het volgende berekend: f - W. Dus de inhoud van W wordt van de inhoud van de file afgetrokken.
Het resultaat wordt geplaatst in W of in f afhankelijk van wat er wordt gekozen voor de waarde van d in de instructie.
Voorbeeld: SUBWF 21h, 1
Door dit uit te voeren zal de ALU een bewerking gaan uitvoeren op de file, het zal de inhoud van W eraf halen en het antwoord terugzetten op de locatie van de file die net is gebruikt.

De volgende serie instructies maken gebruik van digitale logica zoals de functies AND, OR en EXOR.

ANDLW k AND literal with W
ANDWF f, d AND W with f
IORLW k Inclusive OR literal with W
IORWF f, d Inclusive OR W with f
XORLW k Exclusive OR literal with W
XORWF f, d Exclusive OR W with f

Je kunt op deze manier zeer eenvoudig de controller digitale logica laten uitvoeren op data. Inclusive OR is de gewone OR die wij kennen. De Exclusive OR is de EXOR die we wel kennen uit de digitale wereld.
Voorbeeld: ANDWF 24h, 1
Stel in W staat de waarde 3Ah, en in de file op locatie 24h staat 17h. Het antwoord wordt dan 12h, dit zal in de file worden geplaatst omdat d=1 is.
Voorbeeld: XORLW 4Eh
Op deze wijze kun je een getal met de inhoud van W EXOR'ren. Staat er 1Bh in W dan zal na deze instructie W de waarde 55h bevatten.

BTFSC f, b Bit test f, Skip if clear
BTFSS f, b Bit test f, Skip if set

De twee bovenstaande instructies zijn zeer nuttig. Ze testen allebei een bit in een file. Je dient op de plaatst van f aan te geven om welke file het gaat en op de plek van b om welk bit het gaat. De bovenste instructie zal, als het bit dat is getest 0 (dus clear) is, de instructie die volgt overslaan. De BTFSS instructie doet het omgekeerde en zal de opvolgende instructie overslaan in het geval dat het te testen bit 1 (dus geset) was. Deze instructies zijn goed te gebruiken bij het testen van een I/O poort. Door een I/O pin telkens te testen op zijn waarde kun je het programma bij het detecteren bv naar een bepaald stuk programma laten springen.

INCFSZ f, d Increment f, Skip if 0
DECFSZ f, d Decrement f, Skip if 0

Dit zijn 2 instructies die gemakkelijk zijn bij het gebruik van tellers. Ze verhogen namelijk (INCFSZ) of verlagen (DECFSZ) een variabele en zullen zodra de variabele de waarde 0 krijgt de volgende instructie overslaan. Is de waarde 0 nog niet bereikt dan wordt de waarde gewoon opgeslagen en wordt er verder gegaan met de direct daaropvolgende instructies zoals dat gebruikelijk is.

Assembleren

Nu gaan we wat meer uitleggen over wat er nog meer voor mogelijkheden zijn bij het schrijven van een programma.
Het programma dat je schrijft zal aan het einde worden geassembleerd. Dit wil zeggen dat een speciaal programma jouw geschreven code omzet in begrijpbare taal voor de C.
Het speciale programma dat dit kan heet de 'assembler'. De assembler is specifiek gemaakt voor de C die je gebruikt. Voordat een assembler je code om gaat zetten zal het eerst je complete code gaan controleren op fouten, aangezien het natuurlijk geen begrijpbare code voor de controller kan maken als jouw eigen programma fouten bevat. Dit controleren gebeurt op basis van de instructie set. De assembler moet daarom zijn gemaakt voor je controller omdat het moet weten welke instructies er bestaan en hoe ze werken. Om nog meer gebruikersgemak in het ontwikkelen van een programma te krijgen zijn er opties toegevoegd aan de instructies die je mag gebruiken. Zo is er een code bijgevoegd genaamd EQU. Dit is een niet bestaande C instructie maar toch kun je het gebruiken. Met een voorbeeld zullen we verduidelijken wat je ermee kunt.
Stel je wilt de volgende instructie uitvoeren: MOVWF 2Dh (dit zal de waarde van W in de file op adres 2Dh zetten).
Nu zal het beste vaak voorkomen dat je deze instructie gebruikt op ook nog eens telkens hetzelfde adres. Ook zul je deze instructie in je programma meerdere keren gebruiken alleen dan met andere file's. Je zult dus telkens moeten onthouden welk file nummer de waarde van bijvoorbeeld je teller bevatte. Computers zullen dan wel makkelijk met getallen rekenen en doen, wij mensen zijn qua getallen toch wat minder goed. Nu kun je dit vereenvoudigen door de functie EQU te gebruiken. En dat doe je als volgt.
Stel dat je een teller in je programma bijhoudt, die is opgeslagen in de file op adres 2Dh. Je kunt dan helemaal boven in je programma de volgende regel intypen:

Teller	EQU	2Dh

Dit betekent dan dat overal waar jij in je programma invult Teller, dat daar de waarde 2Dh moet komen te staan. Let op, dit is hoofdletter gevoelig.
Zoals we al eerder hebben vertelt is dit geen instructie maar toch kun je het gebruiken omdat dit een speciale optie is die bij de assembler is toegevoegd. Als je je programma gaat assembleren zal de assembler overal waar Teller staat het vervangen door 2Dh en vervolgens deze EQU regel verwijderen omdat het niks voor de C betekent. Op deze manier kun je veel beter en sneller je programma schrijven, omdat je de locatie van de Teller niet meer hoeft te onthouden.
De instructie van het voorbeeld MOVLW 2Dh, kun je nu dus schrijven als MOVLW Teller.

EQU is niet het enigste ongewone commando dat je kunt gebruiken.
Ook het commando END behoort niet tot de instructieset maar dien je wel te gebruiken. Helemaal aan het einde hoor je END neer te zetten. Zo weet de assembler dat dat de plek is waar het programma ophoudt, en dat die dus klaar is met assembleren want er is geen code meer.

Zodra de controller wordt gestart, of gereset, zal deze als eerste de instructie op adres 0000h gaan bekijken. We hebben dit al eerder uitgelegd in het stukje over de program counter. Je moet er dus voor zorgen dat je allereerste uit te voeren instructie op dat adres begint. Dit doe je door de assembler extra informatie aan te bieden. Door het commando ORG kun je dit duidelijk maken.

	ORG	00h

Als je dit helemaal aan het begin van je instructies plaatst dan weet de assembler dat de code die volgt na ORG op adres 00h moet beginnen.

Labels

Niet alleen EQU is extra gemaakt om het jouw te vergemakkelijken. Ook heb je de mogelijkheid om labels te gebruiken. Een label is een naam die je kunt toekennen aan een bepaalde locatie/regel in je geheugen. Wil je bv het commando GOTO gebruiken dan moet je kunnen vertellen waarheen. Hiervoor moet je de regel waar naartoe moet worden gesprongen een naam geven.

		ORG	00h
Teller  	EQU	2Dh
		GOTO	opnieuw
		MOVLW	14h
		ANDLW	40h
opnieuw	        MOVLW	teller
		INCF	teller, 1
		END

Hierboven zie je een klein voorbeeld dat gebruik maakt van labels, van het commando ORG, EQU en END. Het programma slaat nergens op maar het is alleen om de commando's te verduidelijken. GOTO betekent dat er ergens naartoe moet worden gesprongen. Wij vertellen via regel 3 dat er naar 'opnieuw' moet worden gesprongen. Door nu 'opnieuw' voor de regel waar je naar toe wilt springen te zetten weet de controller dat hij daar naartoe moet. In dit geval zal hij dus na het uitvoeren van regel 3 springen naar de regel MOVLW teller en zal vervolgens, zoals normaal, weer regel voor regel verder afwerken. De regels 4 en 5 zullen dus nooit worden uitgevoerd omdat hij daar simpelweg overheen springt.

De wijze van schrijven

Het intypen van je programma is zeer gemakkelijk en net zoals je bent gewent. Gewoon alles achter elkaar typen met een spatie tussen de verschillende onderdelen.
Er is echter een klein verschil. Je programmeerveld is opgedeeld in twee stukken, gescheiden door een TAB. Je moet in het meest linkse gebied de labels plaatsen, heb je geen label dan dien je een TAB vooraf aan je instructie te geven. Met een klein voorbeeld zullen we dit toelichten:

Optellen INCF teller, 1

Dit is met label, namelijk het label Optellen en dus kun je direct erachter na de spatie je instructie typen.
Stel nou dat deze regel geen label had dan werd het als volgt:

   INCF teller,1

Dus eerst een TAB en daarna pas begin je met je commando. De assembler verwacht namelijk in de 1e kolom een label. Je mag, dit hoeft dus niet, tussen het label en tussen je instructienaam een TAB geven. Het maakt het stukje programma namelijk wat overzichtelijker. Zelf doen we dit graag voor de overzichtelijkheid. Voor de assembler maakt het niks uit of je nou:

Optellen INCF teller,1

doet met alleen spatie's of:

Optellen	INCF	Teller, 1

met TAB's ertussen. De keuze is aan jouw.

Nu heb je genoeg kennis om te beginnen met het schrijven van een programma. Het schrijven van een programma kun je simpelweg gewoon in Notepad of een andere teksteditor doen. Je dient het alleen nog wel te assembleren en je wilt het misschien ook nog wel eerst simuleren. Hiervoor heb je een speciaal programma nodig. Dit staat in het volgende stuk van deze tutorial uitgelegd.

Software

In deze tutorial gebruiken we assembler taal. Ik heb hiervoor gekozen omdat deze taal dicht bij de hardware staat. Zo is makkelijker te begrijpen hoe het geheel werkt en zo zijn simpele programma's te schrijven. Naarmate de programma's complexer worden kan worden gekozen voor C++ of een andere hogere programmeertaal. Om een programma in assemblertaal te schrijven hebben we het gratis ter beschikking gestelde programma MPLAB v5.70 van Microchip nodig. Dit is te downloaden op de site van Microchip. MPLAB is hier op onze site te vinden.

Het is een zeer goed programma, wat vele opties bevat. Zo kun je je programma in assembler taal schrijven en vervolgens assembleren. Maar ook nog eens simuleren om te analyseren of het wel correct werkt wat je hebt geschreven. We zullen je duidelijk maken hoe je een programma kunt schrijven, assembleren en vervolgens testen.

Assembler programma editten

Zoals ook in sommige andere programma's dien je te werken met projecten. Daarvoor maken we eerst een nieuw project aan. Dit doe je als volgt.
Kies voor "Project" in de menubalk gevolgt door "New Project�". Blader nu naar de directory waar je de files wilt hebben. Kies nu een filenaam voor je project en druk vervolgens op "OK". Check of er als "Language Tool Suite" voor Mircochip is gekozen, zoniet wijzig dit dan even.
Kies naast Development Mode voor "Change".

Development Mode veranderenDevelopment Mode veranderen

Nu kom je in het "Development Mode" scherm.
Kies in het tabblad "Tools", voor MPLAB-SIM Simulator.
En kies als "Processor:" de PIC16F84A.

De goede processor kiezenDe goede processor kiezen

Ga vervolgens naar het tabblad "Clock".
Stel het "Oscillator Type:" in op "XT".
Vink eerst het vakje MHz aan, zodat je erna bij "Desired Frequency:" 4.000 in kunt vullen.
De andere tabbladen blijven ongewijzigd. Als dit is voltooid kun je op "OK" drukken. Er worden een aantal meldingen gedaan maar die kunnen worden genegeerd.
En er kan net zolang voor "OK" worden gekozen totdat het normale lege programmaveld zichtbaar is.

De clockinstellingenDe clockinstellingen

Kies in de menubalk voor "File" -> "New". Er wordt nu een leeg veld aangemaakt waarin het programma kan worden getypt.
We slaan dit direct weer even op, zodat we straks deze handelingen niet meer hoeven uit te voeren. Kies voor "File" -> "Save As�". Blader nu naar dezelfde directory waar eerder de projectfile was opgeslagen. En kies dezelfde naam voor de file, echter nu gevolgt door ".asm".
Dus als er voor de projectfile was gekozen voor "led.pjt" dan noemen we dit filetje "led.asm".
We kunnen, omdat de .asm file is aangemaakt deze koppelen aan het project. Dit gaat als volgt.
Kies voor "Project" in de menubalk. Klik vervolgens op "Edit Project".
Klik in het scherm wat verschijnt op "Add Node�", en kies voor het .asm filetje wat we net hebben opgeslagen. Nu dit klaar is kunnen we weer terug door "OK" te kiezen.
Vanaf nu weet het programma dat het led.asm filetje moet worden geassembleerd naar led.hex en dat het een onderdeel is van het project.

Nu kan het programma worden geschreven in het grote lege witte veld. Zodra het programma is afgerond, moet het worden gecontroleerd en worden geassembleerd.
Kies voor "Project" -> "Make Project".

Typen van programmaTypen van programma

Hieronder zie je een screenshot van het proces tijdens het assembleren:

Het assemblerenHet assembleren

Zodra er geen errors zijn, wordt er een hex-file aangemaakt. Dit is het filetje dat uiteindelijk in de PIC moet worden geladen. Zijn er wel errors gevonden dan worden deze weergegeven in een ander scherm wat je precies verteld waar de fouten zitten en wat er fout is.

Fouten tijdens het assemblerenFouten tijdens het assembleren

Dit overzicht is een soort logfile. Je ziet dat er eerst wordt begonnen met het builden. En dan gebeuren er dingen tijdens het compilen/assembleren van, in dit voorbeeld verzend.asm.
Er is een warning opgetreden. Dit betekend dat er iets niet helemaal goed en dus je programma niet zal doen wat je verwacht, maar dat de .hex file wel kan worden gemaakt. Ook zie je dat erna een error wordt gevonden. Tussen de haakjes staat op welke regel de code staat waar die fout is gevonden en helemaal aan het einde van de regel vindt je informatie over de foutsoort. In dit geval had ik een opcode (dat is een instructie) gebruikt die niet bestond. Doordat er een error is opgetreden kan de hex file niet worden aangemaakt. En zul je dus eerst die fout moeten oplossen. Aan het einde wordt daarom verteld dat het builden mislukt is.

Simuleren

Met dit programma kun je ook jouw eigen programma simuleren. Je kunt dit doen door instructie voor instructie je programma uit te voeren en in de gaten houden door te kijken naar wat er gebeurt met de geheugens en waardes die de C berekent. Voorlopig gaan we hier echter nog niet op in.

Het programma in de PIC laden

Om een microcontroller toe te kunnen passen in hardware moet hij eerst met het benodigde programma worden geladen.
Dit gebeurt met een speciaal stuk hardware waarmee het programma door middel van serile communicatie met de pc erin gebrand/geprogrammeerd wordt. Je komt ook nog wel eens de parallelle variant tegen voor de overdracht. Deze communicatie gebeurt tussen de pc en de "programmer".

Zodra de geschreven software is getest in bv. MPLAB kan het in de C worden geladen. Er bestaan veel verschillende "programmers". De fabrikant van de chip heeft vaak officiele programmers. Deze kosten echter wel heel wat geld. Ook zijn er veel schema's te vinden op internet van programmers die je zelf in elkaar kunt zetten. Wat al een hoop minder kost, alleen weer wat meer werk. Voor het type C dat wij gebruiken kun je het volgende schema gebruiken. Het is het meest simpele schema dat er is van een "program device" voor de PIC16F84.

Schema van de programmerSchema van de programmer

Waarschuwing: Deze programmer werkt vaak niet goed met laptops en recente computers, en wordt daarom afgeraden.
Een alternatief staat hier.

Dit is het hardware schema dat nodig is om de C te laden met het stukje zelfgeschreven software (zie ook hier). De verbindingen waar een nummer bij staan dienen te worden aangesloten aan de corresponderende DB9 connector pin-nummers. Dit is een connector die je programmer verbindt met de computer. Op de computer moet je een programma draaien dat de hardware aanstuurt en zorgt voor een goede verzending van je programma naar het flash geheugen van de PIC. Het benodigde programma heet IC-Prog. Dit is te downloaden op de IC-Prog site.
Hieronder een screenshot van het programma:

IC-ProgIC-Prog

Voor het programma kan worden gebruikt moeten er eerst wat instellen worden gedaan.
Kies onder "Instellingen" voor de optie "Hardware". Stel alles zo in als hieronder is weergegeven.

IC-Prog instellingenIC-Prog instellingen

Het zou kunnen straks dat blijkt dat er iets niet goed is gegaan met het burnen (om verwarring te voorkomen zal ik het in het vervolg maar zo noemen aangezien de termen 'laden' en 'programmeren' ook wat anders kunnen betekenen). Dat het fout ging komt dan doordat op ieder zijn computer de com poorten anders kunnen zijn toegewezen. Mocht het dus fout gaan tijdens het burnen, kies dan voor een andere com-poort in dit "Hardware instellingen" scherm.
Je kunt nu het scherm weer sluiten door "OK" te drukken.

Ga nu weer naar "Instellingen" en kies deze keer voor "Opties". Kies daar het tabblad waar op staat "Programmeren". Vink alleen de optie "Controle gedurende programmeren" aan. Dit zorgt er voor dat IC-Prog tijdens het programmeren checkt of alles wel goed gaat.

IC-Prog foutcontroleIC-Prog foutcontrole

Nu zijn de hoofdinstellingen klaar en die hoeven in het vervolg niet meer te worden gewijzigd. Nu kunnen we met het echte werk gaan beginnen.
Kies rechtsboven in het pulldown menu voor de PIC 16F84A. Zodat er dan dit komt te staan in de rechterbovenhoek:

De juiste PIC kiezenDe juiste PIC kiezen

Vervolgens gaan we het geassembleerde programma opzoeken.
Kies in het menu voor de optie "Bestand" en dan voor "Openen".
Ga dan naar de map waar je zelfgeschreven programma staat en kies voor het ".hex" bestandje dat de naam heeft van jouw file. Nu zul je zien dat er in het programma opeens allemaal codes in je "Programma Code" veld komen. Dit zijn de codes die jouw geschreven programma vertegenwoordigen, alleen dan in een taal die de C begrijpt. Hier zie je een voorbeeld:

HEX-codeHEX-code

Die blauwe getallen geven de adresnummers aan van het programma geheugen in hex-waarde. Daarnaast zie je telkens hexadecimale codes in stukken van 4 breed. Dit zijn de waardes die in de controller worden geladen. Voor het geval je er baat bij hebt staan er in de rechterrij de ASCII betekenissen van de hex-waarden die links staan. Zo zie op de bovenste rij in het voorbeeld dat de hexcode 16 een letter 'f' vertegenwoordigt. Deze informatie is echter niet van belang voor ons en daarom gaan we weer gauw verder.

Kies nu in het vakje "Oscillator" voor XT. Zo stellen we de oscillator-mode van de C in op "kristal klok". In de datasheet kun je meer informatie vinden over wat de andere keuzes betekenen. Zorg er altijd goed voor dat je de juiste kiest. Doe je dit niet of vergeet je het in te stellen dan heb je kans dat je de C voorgoed beschadigt.

Als laatste instelling dienen we de fuses te kiezen die we wensen te gebruiken. Vink bij de "Fuses" alleen de optie "PWRT" aan. De fuses zijn bepaalde opties die de microcontroller heeft. In deze tutorial ga we alleen in op de PWRT. Dit staat voor Power-up Timer. Deze optie zorgt ervoor dat er gedurende de 72ms nadat de voedingsspanning is aangelegd de C gereset blijft. Dit is handig aangezien de voedingsspanning gedurende die tijd nog instabiel kan zijn en zodanig dan de C niet goed zijn werk kan doen. Om die stabiliteit te waarborgen wordt er een tijdje gewacht alvorens te beginnen met het uitvoeren van het programma.

FusesFuses

Nu zijn we helemaal klaar en kan het 'burnen' beginnen. Zorg ervoor dat de RS232 connector in je pc zit en dat de PIC in het ic-voetje is geplaatst. (let goed op dat je het niet verkeerd om erin hebt gedaan). Druk nu bovenin in de menubalk op "Acties" en kies voor "Programmeren Component". Vervolgens zal door middel van een statusbalk de voortgang worden weergegeven van het 'burnen'. Zodra alles goed is verlopen geeft het programma een melding dat het succesvol is afgerond.

Krijg je een foutmelding aan het einde (of tijdens het 'burnen') dan kan dit verschillende oorzaken hebben.

  • Kijk of je de PIC wel goed in het IC-voetje hebt geplaatst. Dus pin 1 of het kuiltje op het IC aan de kant van de inkeping van het IC-voetje.
  • Kies bij de hardware instellingen voor een andere com-poort.
  • Heb je wel de optie: "Controle gedurende programmeren" aan staan en de andere optie "Controle na programmeren" uit staan?
  • Loop ook even opnieuw de instellingen na voor de zekerheid, misschien ben je wel iets vergeten. Zodra je zeker bent dat alles goed is kun je het weer opnieuw proberen te 'burnen'.

Is het voltooid dan staat je complete programma in het flashgeheugen en blijft dat erop bewaart, zelfs als de voeding eraf gaat. Indien je er later een ander programma in laad dan wordt het oude geheel overschreven.

Politie Project

Aan theorie heb je niet veel. Daarom hebben we aan deze tutorial een project gekoppeld. Het eindresultaat is een schakeling waar de PIC16F84 in zit die 2 leds aanstuurt en laat knipperen. Klinkt misschien heel erg simpel maar qua programmatuur valt dat voor de allereerste keer wel tegen. Het project is ook hier te vinden. Om te beginnen het elektrische schema van het project.

Schema Politie projectSchema Politie project

Zoals je ziet een vrij simpel schema. Het mooie ervan is dat je straks via de programmatuur elk willekeurig knipperpatroon kunt bepalen. Dit is bij een gewoon standaard analoog schema moeilijk tot niet te realiseren.

Het is het makkelijkst als je de datasheet van de PIC er ook bijhaalt zodat je dingen kunt opzoeken. Hier kun je er een downloaden.

Zoals je op de pinout kunt zien gebruiken wij om de led's aan te sturen port A, en hiervan de pennen RA0 en RA1. Dit is belangrijk om te weten omdat we dit in de software dienen aan de geven. De rest van het schema is algemeen voor deze C. De reset wordt hooggetrokken aan de +5v. En via pin 15 en 16 wordt het kloksignaal aangeboden. De 1e regel in je programma is altijd hetzelfde en ook de laatste regel, namelijk:

ORG     00h     ;als eerste
        END     ;als laatste

Zo weet de assembler dat de code die volgt op ORG 00h op adres 00h moet worden geplaatst, dus het begin adres van de adresruimte die beschikbaar is in het ROM van de C. Zodra je de spanning op de controller zet gaat deze als allereerste de code uitvoeren die op plek 00h staat in zijn ROM.

In dit project is ons 1e doel om eerst 1 ledje te laten branden. Dit gaan we dus nu als eerste bekijken. We moeten dus eerst poort A aansturen. Zoals we je hebben uitgelegd in het hardware gedeelte moet je eerst de poorten instellen. Je moet namelijk instellen wat elke pin moet gaan doen. Onze poort A moet de leds aansturen dus moet een uitgang worden. In de datasheet (pag. 7) kun je vinden dat het "data direction register" van poort A op geheugenadres 85h zit. Een 0 in dat register betekend een uitgang en een 1 een ingang. Wij gaan daar dus een 0 neerzetten. Het makkelijkste om dit te doen is om het register te clearen met de instructie CLRF 85h. Omdat het lastig is om die 85h te onthouden maken we gebruik van een EQU commando. Er is alleen een "maar". Niet elk register in het geheugen van de PIC is direct toegankelijk. Dit is ook het geval bij het poort A direction register. Om er toch bij te kunnen moeten we de controller in een andere bank zetten, namelijk bank 1. Dit doe je met de instructie: BSF 03,5. Nadat de richting van poort A (en eventueel B) is ingesteld moet je weer terug naar bank 0 met : BCF 03,5. Dus krijgen we de volgende code krijgen:

instelporta     EQU     85h
		BSF 	03,5
		CLRF    instelporta
		BCF 	03,5

Nu is de poort juist ingesteld en kunnen we hem gaan aansturen. De 1e led die we willen aansturen zit op RA0. Deze led kunnen we laten branden door die uitgang hoog te maken. Hiervoor dienen we het data register van poort A te veranderen. We willen daarvoor op het adres 05h een 1 op het de plek van bit 0. Adres 05h is poort A, en bit 0 is de plek waar de waarde van pin RA0 staat. Hier dient een 1 te komen dus gaan we een 1 daar plaatsen. Dit kan niet direct dus gaan we eerst een getal in W laden, waarna de W zijn waarde op adres 05h laten zetten. Hiervoor gebruiken we de volgens instructies:

porta   EQU     05h
        MOVLW   01h
        MOVWF   porta

01h is binair gezien 00000001. Zo zorgen we er dus voor dat er een 1 op de op de plek van bit 0 komt te staan. Nadat de controller deze instructie heeft uitgevoerd gaat het ledje branden en zal het aanblijven tot we het weer uit doen. Ons doel is het te laten knipperen en zodoende moeten we het dus weer uit doen, en dat geheel herhalen. Het uitdoen gaat hetzelfde als 'aan' alleen dan moeten we een 0 neerzetten in plaats van een 1. Dus we laden W met 00h en zetten dat neer op porta:

        MOVLW   00h
        MOVWF   porta

Nu zal het programma het ledje aan doen en daarna meteen weer uit. Om het te laten knipperen gaan we een GOTO toepassen zodat er opnieuw wordt gesprongen naar het aan en uit gaan, en dan oneindig lang. We voegen daarom de regel:

        GOTO    knipper

Maar we zullen nu ook moeten vertellen waar het label 'knipper' dan moet komen. Wij kiezen voor de plek waar de routine van het 'aan' gaan begint. Hieronder zie je wat we dan totaal al hebben aan code:

		ORG	00h
instelporta	EQU	85h
		BSF 	03,5
		CLRF	instelporta
	        BCF 	03,5
porta		EQU	05h
knipper         MOVLW	01h
		MOVWF	porta
		MOVLW	00h
		MOVWF	porta
		GOTO	knipper
		END

Zoals het er nu staat zal het programma al werken. Het is alleen een goede gewoonte om alle EQU commando's bij elkaar te zetten en dan bij voorkeur boven ORG. Als wij dit toepassen op ons stukje code dan wordt het:

instelporta	EQU	85h
porta		EQU	05h
                ORG	00h
		BSF 	03,5
		CLRF	instelporta
	        BCF 	03,5
knipper         MOVLW	01h
		MOVWF	porta
		MOVLW	00h
		MOVWF	porta
		GOTO	knipper
		END

We zijn echter wel wat vergeten. Deze PIC loopt op een kristal van 4MHz. Elke cycle zal dan 1s duren. In de datasheet kun je zien hoeveel klokcycles een instructie duurt (pag. 36 Table 7-2). Hier vindt je dat (bijna) elke instructie 1 klokcycle duurt. Als we dan ons programma nader bekijken met de snelheid van het uitvoeren in ons achterhoofd dan kom je tot de ontdekking dat het ledje 2s aan is, en 3 s uit. Want er zijn 2 instructies telkens nodig om het 'aan' te krijgen en 3 (incl. de GOTO) om het 'uit' te krijgen. Het ledje zal dus met een cycletijd van 5s knipperen en dit is frequentie van 200kHz en dat kan je niet zien met je oog. Daar moeten we dus een oplossing voor vinden. En deze ligt erg voor de hand. Een aantal niks-doen-instructies kunnen we inbouwen. Zoals de instructie NOP. Dit zorgt wel dat de controller weer 1S in de tijd verder is maar eigenlijk niks doet. Hierbij hebben we echter een klein probleem, we moeten dan zeer veel regels NOP toevoegen willen we een beetje leuke knipperfrequentie krijgen. Daarom gaan we een grote lus inbouwen.

teller	        EQU	40h
teller2	        EQU	41h
                MOVLW   0FFh            ;1 cycle
        	MOVWF	teller2	        ;1 cycle
verder          MOVLW	0FFh	        ;1 cycle
	        MOVWF	teller	        ;1 cycle
opnieuw	        DECFSZ	teller, 1	;1 cycle
	        GOTO	opnieuw		;2 cycles
        	DECFSZ	teller2, 1
        	GOTO	verder

We zullen deze lus even toelichten. De hoofdgedachte is om een getal in op en geheugenplek te zetten, er 1tje af te halen en weer terug te springen, dus 1 eraf, terug 1 eraf terug, enz. en dit heel lang door. Als eerste kijken we even naar teller (dus niet teller2). Je ziet dat achter label 'verder' W wordt geladen met de waarde 0FFh. Deze waarde wordt opgeslagen op de RAM plek die teller heet. Zoals je erboven kunt zien bij de EQU regels is dat geheugenadres 40h. Vervolgens wordt er bij de volgende regel 1 afgetrokken van de waarde die in teller zit. Dat was FFh en wordt dus FEh. Omdat de instructie een 1 heeft aan het einde wordt dit resultaat weer opgeslagen in de file waar het vandaan kwam, dus teller. Ook bekijkt deze instructie meteen of het resultaat 0 is. Is dit het geval dan slaat de controller de volgende instructieregel (GOTO opnieuw) over. Dat is nu nog niet het geval en er wordt dus naar de volgende regel gesprongen. Deze regel vertelt de PIC dat hij naar het label opnieuw moet springen. Dit gaat dus zo door totdat teller 0 wordt, want dan wordt de GOTO overgeslagen. Hoe lang duurt dit nou? Het is uit te rekenen. De 2 regels code:

opnieuw         DECFSZ	teller, 1	;1 cycle
                GOTO	opnieuw		;2 cycles

Duren voor 1 keer uitvoeren 1+2= 3 cycles. Dit stukje wordt 255x uitgevoerd, want FFh is decimaal 255. En het duurt dus 255x voordat teller de waarde 0 zal bereiken. Dus deze 2 regels duren 255x3=765cycles en dat is 765s. Maar dat is nog niet lang genoeg, dus hebben we er nog een lus omheen gebouwd die ervoor zorgt dat die kleine lus die 765s duurt nog eens 255x wordt uitgevoerd. Zo krijg je dus na een ruime berekening 255 x 765 = 195000 cycles. Dit gaat er al op lijken aangezien dit afgerond 200ms zijn. Door deze lus in ons originele programma te stoppen zorgen we ervoor dat het ledje voor ongeveer 200ms aan is. Doen we dit ook aan het einde bij de 'uit' routine dan zal het resultaat zijn dat het ledje met een frequentie van 1 / (200ms + 200ms) = 2,5 Hz gaat knipperen. Het programma gaat er dan als volgt uitzien.

instelporta     EQU	85h
porta		EQU	05h
teller		EQU	40h
teller2	        EQU	41h

                ORG	00h     	;beginadres kiezen

		BSF 	03,5		;kies bank 1
		CLRF	instelporta	;alles pinnen poort A uitgang maken
	        BCF 	03,5		;kies bank 0

knipper	        MOVLW	01h		;laad 01h in W
		MOVWF	porta		;zet de waarde van W
					;in porta, oftewel zet de led aan

                MOVLW	0FFh		;laad 0FFh in W
        	MOVWF	teller2
verder          MOVLW	0FFh
        	MOVWF	teller
opnieuw         DECFSZ	teller, 1
        	GOTO	opnieuw
        	DECFSZ	teller2, 1
        	GOTO	verder

		MOVLW	00h		;laad 00h in W
		MOVWF	porta		;zet de waarde van W
					;in porta, oftwel doe de led uit

                MOVLW	0FFh		;1 cycle
        	MOVWF	teller2		;1 cycle
again		MOVLW	0FFh		;1 cycle
        	MOVWF	teller		;1 cycle
again2	        DECFSZ	teller, 1       ;1 cycle
        	GOTO	again2		;2 cycles
        	DECFSZ	teller2, 1
        	GOTO	again

		GOTO	knipper
		END

Nu het klaar is kunnen we het checken op fouten en kan er een hex-file worden gegenereerd. Dat is het filetje met alle data van ons programma erin en die de PIC begrijpt.
Nadat er geen fouten zijn aangetroffen en de file is aangemaakt kunnen we het in de C gaan laden. In het deel "Het programma in de PIC laden" wordt je uitgelegd hoe dat in zijn werk gaat. Dit bovenstaande programma laat echter maar 1 ledje knipperen. Wil je ze nu na elkaar laten knipperen dan moet je dit programma weer wat uitbreiden. Je kunt dan hetzelfde stuk programma eronder plakken. Omdat je alleen dan een andere uitgang wilt aansturen dien je ipv 0000.0001 op poort A, 0000.0010 neer te zetten. Dus eerst W laden met 02h en dan dit op poort A neerzetten. Let er wel op dat een label maar 1 keer mag voorkomen, dus in het geval dat je hetzelfde programma er onder zet moet je de namen wel wat aanpassen van de labels. Je kunt nu zelf experimenteren met het programma om je eigen knipperpatronen te creren.

Testbordje

Een testbordje is erg handig om je geschreven programma te testen. Of om bijvoorbeeld te leren hoe een microcontroller kan worden toegepast in een schakeling. Je kunt deze bordjes onder andere kopen bij Conrad. Je kunt er je onderdelen in 'prikken' en gemakkelijk weer uit verwijderen. Ze zitten vast in een soort klemmetjes. Hieronder zie je een plaatje van zo'n bordje. Op de voorkant kun je onderdelen zetten. Voor het gemak heb ik ook even de achterkant getekend zodat je ziet hoe de doorverbindingen lopen.

Een testbordjeEen testbordje

Meer info

Wil je meer info over microcontrollers dan kun je op internet zeer veel erover vinden.
Ook zijn er boeken geschreven waarin veel dingen worden opgehelderd.

Heb je vragen hebt naar aanleiding van deze tutorial dan kun je deze in het forum of hieronder kwijt. Let er wel op dat je geen dingen vraagt die je zelf gewoon in deze tutorial kunt vinden.

Links

Handige links voor meer info.

Copyright © 2002-2004 Bastiaan Steenbergen