Gepost door Bastiaan Steenbergen op donderdag 4 december 2003
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.
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 1µs 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 2µs 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 5µs 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 1µS 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 765µs. Maar dat is nog niet lang genoeg, dus hebben we er nog een lus omheen gebouwd die ervoor zorgt dat die kleine lus die 765µs 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 creëren.