Ara volem que el mateix LED estigui encès de manera intermitent. A primer cop d'ull podríem pensar en aquest programa (les línies que estan en color són les noves o modificades respecte a l'anterior):
PROCESSOR 16F690 #include <xc.inc> config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
PSECT code, class=CODE, delta=2, abs ; A l'inici de la memòria main: bsf RP0 ; Tria el banc 1 bcf TRISC,0 ; Posa el bit 0 del port C com a sortida bcf RP0 ; Tria el banc 0 Bucle: bsf PORTC,0 ; Activa el bit 0 del port C bcf PORTC,0 ; Desactiva el bit 0 del port C goto Bucle ; Repetim-ho... END main
Aquest programa té dues diferències respecte a l'anterior. La primera és que després d'activar el LED (bit 0 de PORTC) es torna a desactivar. La segona és que ara es torna a repetir indefinidament el procés d'activació i desactivació i, per fer-ho, el goto va a un lloc diferent que abans.
Si proveu aquest programa veureu que no fa el que esperàvem. El LED està encès tota l'estona encara que, si ens hi fixem bé, veurem que fa menys llum. Per què passa això?
El bucle té tres instruccions. En principi, cada instrucció triga un temps en executar-se, el que anomenem un cicle d'instrucció. Amb la configuració definida del microcontrolador cada instrucció normal triga 1 μs. Les instruccions de salt, com goto, necessiten dos cicles d'instrucció. Així doncs el cicle del nostre programa dura el següent:
| Instrucció | Durada | Estat del LED durant la instrucció |
Temps total |
| bcf PORTC,0 | 1 μs | Encès | 1 μs |
| goto Bucle | 2 μs | Apagat | 3 μs |
| bsf PORTC,0 | 1 μs | Apagat | |
| Total | 4 μs | ||
Dels 4 μs que dura el cicle, el LED està encès la quarta part i apagat la resta. Com el temps és tan breu (la freqüència és tan alta) nosaltres no som capaços de veure que el LED s'encén i s'apaga sinó que el veiem encès però fent menys llum.
Per aconseguir veure el LED encenent-se i apagant-se, cal donar més temps a cada estat. Podem fer un comptador decrementant una variable. Crearem la variable Retard1 i l'anirem decrementant dins un bucle fins que arribi a zero i quan això passi canviarem l'estat del LED. Les nostres variables són d'un byte (és a dir, 8 bits), per tant poden tenir valors entre 0 i 255 (00h a FFh). Quan es decrementa un 00h s'obté un FFh. Farem servir dos bucles, un per encendre el LED i un per apagar el LED.
PROCESSOR 16F690 #include <xc.inc> config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
Retard1 EQU 0x20 ; Definim una variable de comptatge
PSECT code, class=CODE, delta=2, abs ; A l'inici de la memòria
main:
bsf RP0 ; Tria el banc 1
bcf TRISC,0 ; Posa el bit 0 del port C com a sortida
bcf RP0 ; Tria el banc 0
Bucle:
bsf PORTC,0 ; Activa el bit 0 del port C
Ret1:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
bcf PORTC,0 ; Desactiva el bit 0 del port C
Ret2:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
goto Bucle ; Repetim-ho...
END main
Anem a comentar les línies que inclouen novetats significatives respecte al programa anterior. En el tros amb fons blau hem creat la variable Retard1 a l'adreça 0x20 que correspon a la posició 20h que és la primera adreça lliure de la memòria de dades (les adreces de 00h a 1Fh són registres de funcions especials). Podeu trobar l'organització de la memòria de dades aquí.
La instrucció decfsz decrementa la variable i si val zero salta una línia. El primer argument (Retard1 en el nostre cas) indica la variable que es decrementa i el segon (f en aquesta ocasió) indica si el resultat es guarda en la mateixa variable (és el nostre cas, ja que hi hem posat f) o en un registre de treball anomenat acumulador. Si el resultat de l'operació és zero no s'executa la línia següent. En el nostre cas la línia següent és el goto que repeteix el bucle. Com a conseqüència, el bucle es farà 256 cops i quan el registre valgui zero no es farà el salt sinó que es passarà a la instrucció següent (desactivar la sortida).
Si envieu aquest programa, tampoc veureu que el LED s'encengui i apagui. El motiu és que la durada de cada estat és
| Instrucció | Durada | Estat del LED durant la instrucció |
Temps total |
| decfsz Retard1,f | 255 x 1 μs | Encès | 768 μs |
| goto Ret1 | 255 x 2 μs | ||
| decfsz Retard1,f | 2 μs | ||
| bcf PORTC,0 | 1 μs | ||
| decfsz Retard1,f | 255 x 1 μs | Apagat | 770 μs |
| goto Ret2 | 255 x 2 μs | ||
| decfsz Retard1,f | 2 μs | ||
| goto Bucle | 2 μs | ||
| bsf PORTC,0 | 1 μs | ||
| Total | 1538 μs | ||
Ara el temps d'encès i apagat és gairebé el mateix però seguim sense veure res perquè cal que el període superi els 40 ms (freqüència de 25 Hz) per veure alguna cosa.
La versió definitiva del nostre programa tindrà dues variables i dos bucles de decrement, un dins de l'altre. Decrementarem una variable i cada cop que aquesta arribi a zero decrementarem l'altra. Cada cop que la segona arribi a zero canviarem l'estat del LED. El programa serà:
PROCESSOR 16F690 #include <xc.inc> config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
Retard1 EQU 0x20 ; Definim dues variables de comptatge Retard2 EQU 0x21
PSECT code, class=CODE, delta=2, abs ; A l'inici de la memòria
main:
bsf RP0 ; Tria el banc 1
bcf TRISC,0 ; Posa el bit 0 del port C com a sortida
bcf RP0 ; Tria el banc 0
Bucle:
bsf PORTC,0 ; Activa el bit 0 del port C
Ret1:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
decfsz Retard2,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
bcf PORTC,0 ; Desactiva el bit 0 del port C
Ret2:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
decfsz Retard2,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
goto Bucle ; Repetim-ho...
END main
Si envieu aquest nou programa, sí veureu que el LED encendre's i apagar-se. Ara cada iteració del bucle de Retard1 dura 3 μs, per tant les 256 voltes duren 768 μs. A cada volta s'hi afegeixen 3 μs del decrement de Retard2 i el salt del segon bucle. Per tant el segon bucle repeteix 771 μs un total de 256 vegades i, per això, dura 0,19 s. Arrodonint, podem dir que el LED està encès 0,2 s i apagat uns altres 0,2 s, o sigui que fluctua a 2,5 Hz.
Podríem allargar el temps posant una instrucció nop dins el bucle petit (just abans de decfsz Retard1,f). Aquesta instrucció no fa res però consumeix un cicle (1 μs en el nostre cas).
Ara imaginem-nos que volem que en lloc d'un LED se n'encenguin dos; per exemple el 0 i el 2. El programa podria ser el següent.
PROCESSOR 16F690 #include <xc.inc> config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
Retard1 EQU 0x20 ; Definim dues variables de comptatge Retard2 EQU 0x21
PSECT code, class=CODE, delta=2, abs ; A l'inici de la memòria
main:
bsf RP0 ; Tria el banc 1
bcf TRISC,0 ; Posa el bit 0 del port C com a sortida
bcf TRISC,2 ; Posa el bit 2 del port C com a sortida
bcf RP0 ; Tria el banc 0
Bucle:
bsf PORTC,0 ; Activa el bit 0 del port C
bsf PORTC,2 ; Activa el bit 2 del port C
Ret1:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
decfsz Retard2,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
bcf PORTC,0 ; Desactiva el bit 0 del port C
bcf PORTC,2 ; Desactiva el bit 2 del port C
Ret2:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
decfsz Retard2,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
goto Bucle ; Repetim-ho...
END main
Si ho proveu, veureu que no fa el que ens esperàvem. No s'encenen els LED 0 i 2 sinó només el 2. Si proveu de permutar les dues línies de bcf sobre el PORTC resultarà que només s'encén el LED 0. Per què passa això?
L'activació o desactivació de bits sobre els ports no funciona igual com amb la resta de memòria. Quan hi ha una instrucció bcf o bsf el microcontrolador llegeix l'adreça completa (vuit bits), modifica el bit i torna a escriure l'edreça completa. En el cas dels ports, el que es llegeix no és l'estat de les sortides sinó el de les entrades. Així quan fem el segon bsf, es llegeix l'estat de les entrades i com l'entrada RC0 està desactivada tenim un zero en aquesta posició, es canvia el bit 2 i s'escriu.
Per solucionar el problema, podem fer les modificacions sobre una variable i després copiar tota la variable sencera sobre el port. Així si en altres punts del programa es modifica també la variable el que s'enviarà serà el contingut complet. A continuació tenim el programa.
PROCESSOR 16F690 #include <xc.inc> config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
Retard1 EQU 0x20 ; Definim dues variables de comptatge Retard2 EQU 0x21 Port EQU 0x22 ; Variable que guarda l'estat general de les sortides
PSECT code, class=CODE, delta=2, abs ; A l'inici de la memòria
main:
bsf RP0 ; Tria el banc 1
bcf TRISC,0 ; Posa el bit 0 del port C com a sortida
bcf TRISC,2 ; Posa el bit 2 del port C com a sortida
bcf RP0 ; Tria el banc 0
Bucle:
bsf Port,0 ; Activa el bit 0 de la variable Port
bsf Port,2 ; Activa el bit 2 de la variable Port
movf Port,w ; Copia Port a l'acumulador
movwf PORTC ; I ho guarda al PORTC
Ret1:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
decfsz Retard2,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret1 ; Salta, excepte si el resultat ha estat zero
bcf Port,0 ; Desactiva el bit 0 de la variable Port
bcf Port,2 ; Desactiva el bit 2 de la variable Port
movf Port,w ; Copia Port a l'acumulador
movwf PORTC ; I ho guarda al PORTC
Ret2:
decfsz Retard1,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
decfsz Retard2,f ; Decrementa la variable
; si dona zero, no es fa la instrucció següent
goto Ret2 ; Salta, excepte si el resultat ha estat zero
goto Bucle ; Repetim-ho...
END main
Un cop coneixem la forma de fer-ho, ara no seria difícil encendre'n tres o fer que els dos LED s'encenguessin alternadament; per exemple.

Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.