PIC microcontroller tutorial

Gepost door Bastiaan Steenbergen op donderdag 4 december 2003

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 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.