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):
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
org 0 Inici bsf STATUS,RP0 ; Tria el banc 1 bcf TRISC,0 ; Posa el bit 0 del port C com a sortida bcf STATUS,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
Aquest programa té dues diferències respecte a l'anterior. La primera és que després d'activar el LED (bit C0) 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 μss 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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 Retard1 ; Definim una variable de comptatge endc
org 0 Inici bsf STATUS,RP0 ; Tria el banc 1 bcf TRISC,0 ; Posa el bit 0 del port C com a sortida bcf STATUS,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
Anem a comentar les línies que inclouen novetats significatives respecte al programa anterior. La comanda cblock serveix per indicar que comença una definició de variables i l'adreça de la memòria de dades on cal posar-les. En aquest cas hem indicat 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). Només definim la variable Retard1 i acabem la definició amb la instrucció endc. 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 el nostre cas) 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à:
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 Retard1 ; Definim dues variables de comptatge Retard2 endc
org 0 Inici bsf STATUS,RP0 ; Tria el banc 1 bcf TRISC,0 ; Posa el bit 0 del port C com a sortida bcf STATUS,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 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 goto Ret2 ; Salta, excepte si el resultat ha estat zero goto Bucle ; Repetim-ho... end
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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 Retard1 ; Definim dues variables de comptatge Retard2 endc
org 0 Inici bsf STATUS,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 STATUS,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 1 ; 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 2 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 1 ; 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 2 goto Ret2 ; Salta, excepte si el resultat ha estat zero goto Bucle ; Repetim-ho... end
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.
#include <p16F690.inc> __config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
cblock 0x20 Retard1 ; Definim dues variables de comptatge Retard2 Port endc
org 0 Inici bsf STATUS,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 STATUS,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 1 ; 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 2 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 1 ; 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 2 goto Ret2 ; Salta, excepte si el resultat ha estat zero goto Bucle ; Repetim-ho... end

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