Programació en pic-as del PIC 16F690

Referència Trucs Perifèrics   Recursos CITCEA
Tutorial Exemples Projectes   Inici

Exemple DM - Dibuixem sobre la matriu de LED (un sol color)

En aquest exemple farem servir els polsadors per anar movent un cursor (groc) per la matriu de LED i encendre de color blau els punts seleccionats. D'aquesta manera, aconseguirem fer un dibuix. Les funcions assignades a cada polsador són les següents:

Polsador Funció
0 Lliure
1 Desplaçament horitzontal
2 Desplaçament vertical
3 Encendre o apagar LED
4 Lliure
5 Esborrar dibuix

Encara que en aquest programa gastem poca memòria de dades (30 bytes) hem optat per guardar el vector figura, que és el que conté els punts del dibuix, al banc 1 de la memòria; així al banc 0 hi ha més espai disponible per si fem servir aquest programa com a base per a futurs projectes. Amb una idea semblant, hem implementat la consulta del polsador 4, encara que en aquest programa no té cap funció.

Fixem-nos que tots els accessos al vector que guarda el dibuix es fan emprant adreçament indirecte. Atès que l'adreçament indirecte treballa amb adreces de vuit bits, es pot accedir directament a les dades del banc 1 sense necessitat de canviar de banc.

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
Port EQU 0x20  ; Bits a enviar al port B
Sortida EQU 0x21  ; Valors a enviar al MAX7221 (6 bytes, 48 bits)
Compta EQU 0x27  ; Variable per comptar els bits a Envia_max
Compt EQU 0x28  ; Variable per comptar els bytes
Comp EQU 0x29  ; Variable per comptar els bits
Filera EQU 0x2A  ; Variable per comptar fileres
Actiu EQU 0x2B  ; Variable que diu quin color està actiu
			; Actiu = 0		Apagat
			; Actiu = 1		Vermell
			; Actiu = 2		Verd
			; Actiu = 3		Blau
Polsador EQU 0x2C  ; Variable que conté un bit per a cada polsador per saber si està premut	
x EQU 0x2D  ; Coordenada X del cursor (0 a 7)
			; X = 0 és la columna de la dreta
y EQU 0x2E  ; Coordenada Y del cursor (0 a 7)
caux EQU 0x2F  ; Comptador auxiliar
mirar EQU 0x30  ; Espera que es deixi anar el polsador
mascara EQU 0x31  ; Màscara per a les operacions amb bits
Retard1 EQU 0x32  ; Variables per als cicles de retard
Retard2 EQU 0x33
; Zona de memòria de dades que no depèn del banc triat
W_Copia EQU 0x70  ; Guardarà el contingut de W durant la interrupció
ST_Copia EQU 0x71  ; Guardarà STATUS durant la interrupció 
; Zona de memòria de dades del banc 1
figura EQU 0xA0  ; Aquí guardarem el dibuix (8 bytes, 8 fileres)
PSECT code, class=CODE, delta=2, abs
main:  ; Adreça 0 h
  goto	Inici  ; Saltem al lloc on hi ha el programa principal
  nop  ; Zona de memòria de programa que no utilitzem
  nop
  nop
Interrup:  ; Adreça 4 h
  movwf W_Copia  ; Copiem l'acumulador a W_Copia
  swapf STATUS,w  ; Copiem STATUS a l'acumulador permutant els nibbles
  clrf STATUS  ; Posa a 0 i així segur que el banc és el 0
  movwf ST_Copia  ; Guarda STATUS permutat a ST_Copia
  btfss T0IF  ; Mira si Timer0 ha arribat a zero
              ; Si hi ha arribat, no fa la instrucció següent
  goto FiInt  ; Si la interrupció no és del Timer0 no fem res
Timer0:  ; Programa corresponent a Timer0
  bcf T0IF  ; Si ha arribat, desactivem el bit
  movlw 100  ; Nova preselecció
  movwf TMR0  ; Ho posem a TMR0
              ; Anem a gestionar el canvi de color
  movlw 00000011B  ; Descarta els bits que no serveixen
  andwf Actiu,f
  btfsc ZERO
  goto NoZero  ; Si Actiu val 0 no ho canvia
  decf Actiu,f  ; Passem a activar un altre color
  btfss ZERO  ; Si s'activa Z és que hem arribat a zero
  goto NoZero  ; Si és zero, tornem a 3
  movlw 3
  movwf Actiu
NoZero:  ; Gestiona l'activació de tres MAX7221
         ; Segons Actiu, activa un color o un altre
  movlw 00000011B  ; Descarta els bits que no serveixen
  andwf Actiu,f
  btfsc ZERO  ; Salta si el resultat no és zero
  goto Apagat  ; Si està apagat ho desactiva tot
  movlw 1
  xorwf Actiu,w
  btfsc ZERO
  goto Vermell  ; Si val 1, activem el vermell
  movlw 2
  xorwf Actiu,w
  btfsc ZERO
  goto Verd  ; Si val 2, activem el verd
             ; Si no és cap d'ells, activem el blau
  movlw 0x01  ; Activat
  movwf Sortida+4  ; Blau
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Vermell
  movwf Sortida+2  ; Verd
  movlw 1
  goto Activa
Vermell:
  movlw 0x01  ; Activat
  movwf Sortida  ; Vermell
  movlw 0x00  ; Desactivat
  movwf Sortida+2  ; Verd
  movwf Sortida+4  ; Blau
  nop  ; Temps a afegir
  nop
  nop
  nop
  goto Activa
Verd:
  movlw 0x01  ; Activat
  movwf Sortida+2  ; Verd
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Vermell
  movwf Sortida+4  ; Blau
  goto Activa
Apagat:
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Vermell
  movwf Sortida+2  ; Verd
  movwf Sortida+4  ; Blau
Activa:
  movlw 0x0C  ; Shutdown mode
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia als MAX7221
FiInt:
  swapf ST_Copia,w  ; Copia permutant ST_Copia a l'acumulador
  movwf STATUS  ; I ho passa a STATUS recuperant el valor d'abans
                ; de la interrupció
  swapf W_Copia,f  ; Permuta els bits de W_Copia
  swapf W_Copia,w  ; Torna a permutar els bits de W_Copia
                   ; i els guarda a l'acumulador sense variar STATUS
  retfie  ; Torna al programa principal, on s'havia quedat
Inici:
  bsf RP0  ; Tria el banc 1
  movlw 10000101B  ; Configuració de Timer0
                   ; Com a temporitzador basat en rellotge
                   ; 101 - Factor d'escala de 64
                   ; I resistències de pull-up desactivades (valor per defecte)
  movwf OPTION_REG  ; Ho guarda al registre de configuració del Timer0
  movlw 0xFF  ; Posa l'acumulador a FFh (tot uns)
  movwf TRISA  ; Posa tots els bits del port A com a entrada
  clrf TRISB  ; Posa tots els bits del port B com a sortida
  clrf TRISC  ; Posa tots els bits del port C com a sortida
  movlw 00010000B
  movwf ADCON1  ; Posa el conversor a 1/8 de la freqüència
  bcf RP0
  bsf RP1  ; Tria el banc 2
  movlw 00000101B
  movwf ANSEL  ; Configura AN0 i AN2 com entrada analògica
  bcf RP0
  bcf RP1  ; Tria el banc 0
  clrf PORTC
  clrf Port  ; Posa a zero tots els bits de la variable Port
  movf Port,w
  movwf PORTB  ; I ho envia al port B
  movlw 00001001B
  movwf ADCON0  ; Activa el conversor A/D connectat a AN2
                ; amb el resultat justificat per l'esquerra
  call Ini3max  ; Inicialitza la matriu
                ; Configura Timer 0 i activa la matriu i les interrupcions
                ; Una interrupció cada, aproximadament, 10 ms
  movlw 1
  movwf Actiu  ; Posa en marxa el vermell
  movlw 100  ; Presselecció de 100, que són 156 iteracions
  movwf TMR0  ; Ho posa com a preselecció del temporitzador
  movlw 10100000B  ; Activem GIE i T0IE
  movwf INTCON  ; Activa les interrupcions globals i la de Timer0
  call Apaga  ; Apaga els LED
  clrf x  ; Posa x a zero
  clrf y  ; Posa y a zero
  movlw 1
  movwf mirar  ; Posa mirar a 1
  call PosaZero  ; Posa a zero els bytes del vector figura
Bucle:
  ; Mirem els polsadors
  ; Un cop s'ha fet l'acció del polsador, no es tornarà a fer fins
  ; que es detecti que s'han deixat anar
  call Llegir  ; Llegeix els polsadors
  movf mirar,f  ; S'han de mirar els polsadors?
  btfsc ZERO  ; Si és zero no s'han de mirar
  goto NoMirar  ; Anem a veure si ja s'ha deixat anar
  movf Polsador,w
  xorlw 00000001B  ; Mirem si s'ha premut el polsador 1
  btfss ZERO  ; Si és l'1, salta
  goto NoEsPol1  ; No és l'1
               ; S'ha premut el polsador 1
  incf x,f  ; Incrementem x del cursor
  movlw 00000111B  ; Valor 7
  andwf x,f  ; Limitem x entre 0 i 7
  goto Zmirar  ; Ja estem del polsador 1
NoEsPol1:
  movf Polsador,w
  xorlw 00000010B  ; Mirem si s'ha premut el polsador 2
  btfss ZERO  ; Si és el 2, salta
  goto NoEsPol2  ; No és el 2
               ; S'ha premut el polsador 2
  incf y,f ; Incrementem y del cursor
  movlw 00000111B  ; Valor 7
  andwf y,f  ; Limitem x entre 0 i 7
  goto Zmirar  ; Ja estem del polsador 2
NoEsPol2:
  movf Polsador,w
  xorlw 00000100B  ; Mirem si s'ha premut el polsador 3
  btfss ZERO  ; Si és el 3, salta
  goto NoEsPol3  ; No és el 3
               ; S'ha premut el polsador 3
  movlw 00000001B  ; Posem un 1 a la columna 0
  movwf mascara  ; Ho guardem com a màscara
  movf x,w  ; Agafem la x del cursor
  btfsc ZERO  ; Mirem si és zero
  goto xZero  ; Si és zero no cal fer res
  movwf Comp  ; Hem de rodar els bits x posicions
rodaX:
  bcf CARRY  ; Volem entrar zeros
  rlf mascara,f  ; Rodem a l'esquerra i entrem el zero
  decfsz Comp,f  ; Decrementa Comp
  goto rodaX  ; Si Comp no és zero, repeteix el bucle
xZero:  ; Ja tenim l'1 posat a la columna
  movlw figura  ; Agafem l'adreça de la primera posició del vector
  movwf FSR  ; La posem com a punter d'adreçament indirecte
  movf y,w  ; Agafem la y del cursor
  addwf FSR,f  ; Ho afegim al punter
  movf mascara,w  ; Agafa la màscara
  xorwf INDF,f  ; L'aplica a la filera corresponent
  goto Zmirar  ; Ja estem del polsador 3
NoEsPol3:
  movf Polsador,w
  xorlw 00001000B  ; Mirem si s'ha premut el polsador 4
  btfss ZERO  ; Si és el 4, salta
  goto NoEsPol4  ; No és el 4
               ; S'ha premut el polsador 4
               ; El polsador 4 no fa res
  goto Zmirar  ; Ja estem del polsador 4
NoEsPol4:
  movf Polsador,w
  xorlw 00010000B  ; Mirem si s'ha premut el polsador 5
  btfss ZERO  ; Si és el 5, salta
  goto NoEsPol5  ; No és el 5
               ; S'ha premut el polsador 5
  call PosaZero  ; Posa a zero els bytes del vector figura
Zmirar:  ; Ja estem del polsador 5
  clrf mirar  ; Posa mirar a zero fins que es deixi el polsador
NoEsPol5:
  goto Matriu  ; Envia la figura actual a la matriu
NoMirar:
  movf Polsador,w  ; Agafem la lectura dels polsadors
  btfss ZERO  ; Mira si és zero
  goto Matriu  ; Si no és zero cal seguir esperant
  movlw 1  ; Si és zero, s'ha deixat el polsador
  movwf mirar  ; Posa mirar a 1
Matriu:  ; Anem a mostrar la figura actual a la matriu de LED
  clrf caux  ; Primer enviem la filera 0
  movlw 8  ; Número de bytes que cal enviar
  movwf Compt  ; Variable per comptar els bytes
  movlw figura  ; Agafem l'adreça de la primera posició del vector
  movwf FSR  ; La posem com a punter d'adreçament indirecte
BucleMatriu:
  clrf Sortida  ; Vermell, en principi, a zero
  clrf Sortida+2  ; Verd, en principi, a zero
  movf INDF,w  ; Agafa la filera actual
  movwf Sortida+4  ; La posa en el blau
  ; Anem a veure si estem a la filera del cursor
  movf caux,w  ; Mirem la filera que estem enviant
  xorwf y,w  ; I comparem amb la del cursor
  btfss ZERO  ; Si és zero, són iguals
  goto noCur  ; No és la filera del cursor
  movlw 00000001B  ; Posem un 1 a la columna 0
  movwf mascara  ; Ho guardem com a màscara
  movf x,w  ; Agafem la x del cursor
  btfsc ZERO  ; Mirem si és zero
  goto xnul  ; Si és zero no cal fer res
  movwf Comp  ; Hem de rodar els bits x posicions
giraX:
  bcf CARRY  ; Volem entrar zeros
  rlf mascara,f  ; Rodem a l'esquerra i entrem el zero
  decfsz Comp,f  ; Decrementa Comp
  goto giraX  ; Si Comp no és zero, repeteix el bucle
xnul:  ; Ja tenim l'1 posat a la columna
  movf mascara,w  ; Agafem la màscara que hem preparat (set 0 i un 1)
  iorwf Sortida,f  ; L'apliquem al vermell
  iorwf Sortida+2,f  ; i al verd, per fer el cursor groc
  comf mascara,w  ; Agafem l'invers de la màscara (set 1 i un 0)
  andwf Sortida+4,f  ; L'apliquem al blau per apagar el LED
; Fi del tractament del cursor
noCur:
  ; A la matriu s'hi envien valors entre 1 i 8 (no de 0 a 7)
  incf caux,w  ; Incrementa l'índex i el guarda a w
  movwf Sortida+1  ; El posa per indicar la filera que escrivim
  movwf Sortida+3  ; El posa per indicar la filera que escrivim
  movwf Sortida+5  ; El posa per indicar la filera que escrivim
  call Envia3max  ; Ho envia al MAX7221
  incf FSR,f  ; Incrementa el punter
  incf caux,f  ; Incrementa l'índex
  decfsz Compt,f  ; Decrementa Compt
  goto BucleMatriu  ; Si Compt no és zero, repeteix el bucle
  movlw 1  ; Retard de 0,771 ms
  call Retms
  goto Bucle  ; Torna a repetir
;
; Posa a zero els bytes del vector figura
;
PosaZero:
  movlw 8  ; Número de bytes que cal posar a zero
  movwf Compt  ; Variable per comptar els bytes
  movlw figura  ; Agafem l'adreça de la primera posició del vector
  movwf FSR  ; La posem com a punter d'adreçament indirecte
BucleZero:
  clrf INDF  ; Posa a zero la posició actual
  incf FSR,f  ; Incrementa el punter
  decfsz Compt,f  ; Decrementa Compt
  goto BucleZero  ; Si Compt no és zero, repeteix el bucle
  return
;
; Funció que llegeix els polsadors
;
Llegir:
  clrf Polsador  ; Si aquest valor no es canvia és que no hi ha cap polsador premut
  nop  ; espera un microsegon
  nop  ; espera un microsegon
  nop  ; espera un microsegon
  nop  ; espera un microsegon
  nop  ; espera un microsegon, en total 5
  bsf GO_DONE  ; Inicia la conversió
  btfsc GO_DONE  ; Quan el bit sigui 0 la conversió haurà acabat
  goto $-1  ; repetim la línia fins que deixi de ser 0
            ; Comparació P1
  movlw 220  ; Límit superior de P1
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP1  ; ADRESH >= W
  movlw 200  ; Límit inferior de P1
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP1  ; ADRESH < W
  movlw 00000001B  ; Polsador P1
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP1:  ; Comparació P2
  movlw 194  ; Límit superior de P2
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP2  ; ADRESH >= W
  movlw 174  ; Límit inferior de P2
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP2  ; ADRESH < W
  movlw 00000010B  ; Polsador P2
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP2:  ; Comparació P3
  movlw 163  ; Límit superior de P3
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP3  ; ADRESH >= W
  movlw 143  ; Límit inferior de P3
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP3  ; ADRESH < W
  movlw 00000100B  ; Polsador P3
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP3:  ; Comparació P4
  movlw 90  ; Límit superior de P4
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP4  ; ADRESH >= W
  movlw 70  ; Límit inferior de P4
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP4  ; ADRESH < W
  movlw 00001000B  ; Polsador P4
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP4:  ; Comparació P5
  movlw 55  ; Límit superior de P5
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP5  ; ADRESH >= W
  movlw 35  ; Límit inferior de P5
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP5  ; ADRESH < W
  movlw 00010000B  ; Polsador P5
  movwf Polsador  ; Ho copia a Polsador
NoEsP5:
  return
;
; Apaga tots els LED
;
Apaga:
  movlw 8  ; Hem d'apagar vuit fileres
  movwf Filera  ; Ho guarda al comptador
Repetir:
  movf Filera,w  ; Filera actual
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 00000000B  ; Apagat				
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia3max  ; Ho envia al MAX7221
  decfsz Filera,f  ; Passem a una altra filera
  goto Repetir
  return
;
; Inicialització de tres MAX7221
;
Ini3max:
  movlw 0x0C  ; Shutdown mode
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia al MAX7221
  movlw 0x09  ; Decode mode
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 0x00  ; No decode
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia al MAX7221
  movlw 0x0B  ; Scan limit
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 0x07  ; Vuit fileres
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia al MAX7221
  return
;
; Envia dades (48 bits) a tres MAX7221
; desactivant les interrupcions
;
Envia3max:
  bcf T0IE  ; Desactiva les interrupcions momentàniament
  call Envia_max
  bsf T0IE  ; Reactiva les interrupcions a l'acabar
  return
;
; Envia dades (48 bits) a tres MAX7221
;
; Els bits estan a les variables:
; Sortida	Valor vermells
; Sortida+1	Adreça vermells
; Sortida+2	Valor verds
; Sortida+3	Adreça verds
; Sortida+4	Valor blaus
; Sortida+5	Adreça blaus
; Al final de la funció, el valor de Sortida queda corromput
; Si es vol conservar, cal copiar-lo a una altra variable
;
; Aquesta funció dura, aproximadament, 1 ms
Envia_max:
  bcf Port,5  ; S'assegura que Clock està desactivat
  bcf Port,6  ; S'assegura que Latch està desactivat
  movf Port,w  ; Agafa el valor de Port
  movwf PORTB  ; I el posa al port B
  movlw 48  ; Número de bits a enviar
  movwf Compta  ; Variable per comptar els bits
Bucle3max:
  bcf Port,4  ; Desactiva Data. Si toca activar-ho, ja ho farem
  rlf Sortida,f  ; Fa sortir el bit de més a l'esquerra cap a C
  rlf Sortida+1,f  ; i roda els altres a l'esquerra
  rlf Sortida+2,f
  rlf Sortida+3,f
  rlf Sortida+4,f
  rlf Sortida+5,f
  btfsc CARRY  ; Mira si el bit de l'esquerra era un 1
  bsf Port,4  ; Si era 1, activa Data
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Data
  movwf PORTB  ; I el posa al port B
  bsf Port,5  ; Activa Clock, forçant a llegir el bit
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Clock
  movwf PORTB  ; I el posa al port B
  bcf Port,5  ; Desactiva Clock
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Clock
  movwf PORTB  ; I el posa al port B
  decfsz Compta,f  ; Decrementa Compta
  goto Bucle3max  ; Si Compta no és zero, repeteix el bucle
  bsf Port,6  ; Torna a activar Latch
              ; Els valors es copiaran a la sortida del registre
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Latch
  movwf PORTB  ; I el posa al port B
  return
;
; Funció de retard de 0,771 W ms
;
Retms:
  movwf Retard2
Buclems:
  decfsz Retard1,f
  goto Buclems
  decfsz Retard2,f
  goto Buclems
  return
END main

La següent versió del programa és molt similar però mostra el cursor intermitent. Per fer-ho, decrementa un comptador a cada cicle i quan s'assoleix el nombre de cicles canvia la variable que controla si el cursor està encès o apagat.

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
#define cic_int 35  ; Nombre de cicles per a la intermitència
Port EQU 0x20  ; Bits a enviar al port B
Sortida EQU 0x21  ; Valors a enviar al MAX7221 (6 bytes, 48 bits)
Compta EQU 0x27  ; Variable per comptar els bits a Envia_max
Compt EQU 0x28  ; Variable per comptar els bytes
Comp EQU 0x29  ; Variable per comptar els bits
Filera EQU 0x2A  ; Variable per comptar fileres
Actiu EQU 0x2B  ; Variable que diu quin color està actiu
			; Actiu = 0		Apagat
			; Actiu = 1		Vermell
			; Actiu = 2		Verd
			; Actiu = 3		Blau
Polsador EQU 0x2C  ; Variable que conté un bit per a cada polsador per saber si està premut	
x EQU 0x2D  ; Coordenada X del cursor (0 a 7)
			; X = 0 és la columna de la dreta
y EQU 0x2E  ; Coordenada Y del cursor (0 a 7)
caux EQU 0x2F  ; Comptador auxiliar
mirar EQU 0x30  ; Espera que es deixi anar el polsador
mascara EQU 0x31  ; Màscara per a les operacions amb bits
compt_int EQU 0x32  ; Comptador de cicles per a la intermitència
cur_on EQU 0x33  ; Controla l'estat del cursor
Retard1 EQU 0x34  ; Variables per als cicles de retard
Retard2 EQU 0x35
; Zona de memòria de dades que no depèn del banc triat
W_Copia EQU 0x70  ; Guardarà el contingut de W durant la interrupció
ST_Copia EQU 0x71  ; Guardarà STATUS durant la interrupció 
; Zona de memòria de dades del banc 1
figura EQU 0xA0  ; Aquí guardarem el dibuix (8 bytes, 8 fileres)
PSECT code, class=CODE, delta=2, abs
main:  ; Adreça 0 h
  goto	Inici  ; Saltem al lloc on hi ha el programa principal
  nop  ; Zona de memòria de programa que no utilitzem
  nop
  nop
Interrup:  ; Adreça 4 h
  movwf W_Copia  ; Copiem l'acumulador a W_Copia
  swapf STATUS,w  ; Copiem STATUS a l'acumulador permutant els nibbles
  clrf STATUS  ; Posa a 0 i així segur que el banc és el 0
  movwf ST_Copia  ; Guarda STATUS permutat a ST_Copia
  btfss T0IF  ; Mira si Timer0 ha arribat a zero
              ; Si hi ha arribat, no fa la instrucció següent
  goto FiInt  ; Si la interrupció no és del Timer0 no fem res
Timer0:  ; Programa corresponent a Timer0
  bcf T0IF  ; Si ha arribat, desactivem el bit
  movlw 100  ; Nova preselecció
  movwf TMR0  ; Ho posem a TMR0
              ; Anem a gestionar el canvi de color
  movlw 00000011B  ; Descarta els bits que no serveixen
  andwf Actiu,f
  btfsc ZERO
  goto NoZero  ; Si Actiu val 0 no ho canvia
  decf Actiu,f  ; Passem a activar un altre color
  btfss ZERO  ; Si s'activa Z és que hem arribat a zero
  goto NoZero  ; Si és zero, tornem a 3
  movlw 3
  movwf Actiu
NoZero:  ; Gestiona l'activació de tres MAX7221
         ; Segons Actiu, activa un color o un altre
  movlw 00000011B  ; Descarta els bits que no serveixen
  andwf Actiu,f
  btfsc ZERO  ; Salta si el resultat no és zero
  goto Apagat  ; Si està apagat ho desactiva tot
  movlw 1
  xorwf Actiu,w
  btfsc ZERO
  goto Vermell  ; Si val 1, activem el vermell
  movlw 2
  xorwf Actiu,w
  btfsc ZERO
  goto Verd  ; Si val 2, activem el verd
             ; Si no és cap d'ells, activem el blau
  movlw 0x01  ; Activat
  movwf Sortida+4  ; Blau
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Vermell
  movwf Sortida+2  ; Verd
  movlw 1
  goto Activa
Vermell:
  movlw 0x01  ; Activat
  movwf Sortida  ; Vermell
  movlw 0x00  ; Desactivat
  movwf Sortida+2  ; Verd
  movwf Sortida+4  ; Blau
  nop  ; Temps a afegir
  nop
  nop
  nop
  goto Activa
Verd:
  movlw 0x01  ; Activat
  movwf Sortida+2  ; Verd
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Vermell
  movwf Sortida+4  ; Blau
  goto Activa
Apagat:
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Vermell
  movwf Sortida+2  ; Verd
  movwf Sortida+4  ; Blau
Activa:
  movlw 0x0C  ; Shutdown mode
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia als MAX7221
FiInt:
  swapf ST_Copia,w  ; Copia permutant ST_Copia a l'acumulador
  movwf STATUS  ; I ho passa a STATUS recuperant el valor d'abans
                ; de la interrupció
  swapf W_Copia,f  ; Permuta els bits de W_Copia
  swapf W_Copia,w  ; Torna a permutar els bits de W_Copia
                   ; i els guarda a l'acumulador sense variar STATUS
  retfie  ; Torna al programa principal, on s'havia quedat
Inici:
  bsf RP0  ; Tria el banc 1
  movlw 10000101B  ; Configuració de Timer0
                   ; Com a temporitzador basat en rellotge
                   ; 101 - Factor d'escala de 64
                   ; I resistències de pull-up desactivades (valor per defecte)
  movwf OPTION_REG  ; Ho guarda al registre de configuració del Timer0
  movlw 0xFF  ; Posa l'acumulador a FFh (tot uns)
  movwf TRISA  ; Posa tots els bits del port A com a entrada
  clrf TRISB  ; Posa tots els bits del port B com a sortida
  clrf TRISC  ; Posa tots els bits del port C com a sortida
  movlw 00010000B
  movwf ADCON1  ; Posa el conversor a 1/8 de la freqüència
  bcf RP0
  bsf RP1  ; Tria el banc 2
  movlw 00000101B
  movwf ANSEL  ; Configura AN0 i AN2 com entrada analògica
  bcf RP0
  bcf RP1  ; Tria el banc 0
  clrf PORTC
  clrf Port  ; Posa a zero tots els bits de la variable Port
  movf Port,w
  movwf PORTB  ; I ho envia al port B
  movlw 00001001B
  movwf ADCON0  ; Activa el conversor A/D connectat a AN2
                ; amb el resultat justificat per l'esquerra
  call Ini3max  ; Inicialitza la matriu
                ; Configura Timer 0 i activa la matriu i les interrupcions
                ; Una interrupció cada, aproximadament, 10 ms
  movlw 1
  movwf Actiu  ; Posa en marxa el vermell
  movlw 100  ; Presselecció de 100, que són 156 iteracions
  movwf TMR0  ; Ho posa com a preselecció del temporitzador
  movlw 10100000B  ; Activem GIE i T0IE
  movwf INTCON  ; Activa les interrupcions globals i la de Timer0
  call Apaga  ; Apaga els LED
  clrf x  ; Posa x a zero
  clrf y  ; Posa y a zero
  movlw 1
  movwf mirar  ; Posa mirar a 1
  movwf cur_on  ; Posa cur_on a 1
  movlw cic_int  ; Nombre de cicles per a la intermitència
  movwf compt_int
  call PosaZero  ; Posa a zero els bytes del vector figura
Bucle:
  ; Mirem els polsadors
  ; Un cop s'ha fet l'acció del polsador, no es tornarà a fer fins
  ; que es detecti que s'han deixat anar
  call Llegir  ; Llegeix els polsadors
  movf mirar,f  ; S'han de mirar els polsadors?
  btfsc ZERO  ; Si és zero no s'han de mirar
  goto NoMirar  ; Anem a veure si ja s'ha deixat anar
  movf Polsador,w
  xorlw 00000001B  ; Mirem si s'ha premut el polsador 1
  btfss ZERO  ; Si és l'1, salta
  goto NoEsPol1  ; No és l'1
               ; S'ha premut el polsador 1
  incf x,f  ; Incrementem x del cursor
  movlw 00000111B  ; Valor 7
  andwf x,f  ; Limitem x entre 0 i 7
  goto Zmirar  ; Ja estem del polsador 1
NoEsPol1:
  movf Polsador,w
  xorlw 00000010B  ; Mirem si s'ha premut el polsador 2
  btfss ZERO  ; Si és el 2, salta
  goto NoEsPol2  ; No és el 2
               ; S'ha premut el polsador 2
  incf y,f ; Incrementem y del cursor
  movlw 00000111B  ; Valor 7
  andwf y,f  ; Limitem x entre 0 i 7
  goto Zmirar  ; Ja estem del polsador 2
NoEsPol2:
  movf Polsador,w
  xorlw 00000100B  ; Mirem si s'ha premut el polsador 3
  btfss ZERO  ; Si és el 3, salta
  goto NoEsPol3  ; No és el 3
               ; S'ha premut el polsador 3
  movlw 00000001B  ; Posem un 1 a la columna 0
  movwf mascara  ; Ho guardem com a màscara
  movf x,w  ; Agafem la x del cursor
  btfsc ZERO  ; Mirem si és zero
  goto xZero  ; Si és zero no cal fer res
  movwf Comp  ; Hem de rodar els bits x posicions
rodaX:
  bcf CARRY  ; Volem entrar zeros
  rlf mascara,f  ; Rodem a l'esquerra i entrem el zero
  decfsz Comp,f  ; Decrementa Comp
  goto rodaX  ; Si Comp no és zero, repeteix el bucle
xZero:  ; Ja tenim l'1 posat a la columna
  movlw figura  ; Agafem l'adreça de la primera posició del vector
  movwf FSR  ; La posem com a punter d'adreçament indirecte
  movf y,w  ; Agafem la y del cursor
  addwf FSR,f  ; Ho afegim al punter
  movf mascara,w  ; Agafa la màscara
  xorwf INDF,f  ; L'aplica a la filera corresponent
  goto Zmirar  ; Ja estem del polsador 3
NoEsPol3:
  movf Polsador,w
  xorlw 00001000B  ; Mirem si s'ha premut el polsador 4
  btfss ZERO  ; Si és el 4, salta
  goto NoEsPol4  ; No és el 4
               ; S'ha premut el polsador 4
               ; El polsador 4 no fa res
  goto Zmirar  ; Ja estem del polsador 4
NoEsPol4:
  movf Polsador,w
  xorlw 00010000B  ; Mirem si s'ha premut el polsador 5
  btfss ZERO  ; Si és el 5, salta
  goto NoEsPol5  ; No és el 5
               ; S'ha premut el polsador 5
  call PosaZero  ; Posa a zero els bytes del vector figura
Zmirar:  ; Ja estem del polsador 5
  clrf mirar  ; Posa mirar a zero fins que es deixi el polsador
NoEsPol5:
  goto Matriu  ; Envia la figura actual a la matriu
NoMirar:
  movf Polsador,w  ; Agafem la lectura dels polsadors
  btfss ZERO  ; Mira si és zero
  goto Matriu  ; Si no és zero cal seguir esperant
  movlw 1  ; Si és zero, s'ha deixat el polsador
  movwf mirar  ; Posa mirar a 1
Matriu:  ; Anem a mostrar la figura actual a la matriu de LED
  clrf caux  ; Primer enviem la filera 0
  movlw 8  ; Número de bytes que cal enviar
  movwf Compt  ; Variable per comptar els bytes
  movlw figura  ; Agafem l'adreça de la primera posició del vector
  movwf FSR  ; La posem com a punter d'adreçament indirecte
BucleMatriu:
  clrf Sortida  ; Vermell, en principi, a zero
  clrf Sortida+2  ; Verd, en principi, a zero
  movf INDF,w  ; Agafa la filera actual
  movwf Sortida+4  ; La posa en el blau
  ; Mirem si s'ha de mostrar el cursor
  movf cur_on,w  ; Mirem el valor de cur_on
  btfsc ZERO  ; Si no és zero, mostrem el cursor
  goto noCur  ; Si és zero no hem de mostrar el cursor
  ; Anem a veure si estem a la filera del cursor
  movf caux,w  ; Mirem la filera que estem enviant
  xorwf y,w  ; I comparem amb la del cursor
  btfss ZERO  ; Si és zero, són iguals
  goto noCur  ; No és la filera del cursor
  movlw 00000001B  ; Posem un 1 a la columna 0
  movwf mascara  ; Ho guardem com a màscara
  movf x,w  ; Agafem la x del cursor
  btfsc ZERO  ; Mirem si és zero
  goto xnul  ; Si és zero no cal fer res
  movwf Comp  ; Hem de rodar els bits x posicions
giraX:
  bcf CARRY  ; Volem entrar zeros
  rlf mascara,f  ; Rodem a l'esquerra i entrem el zero
  decfsz Comp,f  ; Decrementa Comp
  goto giraX  ; Si Comp no és zero, repeteix el bucle
xnul:  ; Ja tenim l'1 posat a la columna
  movf mascara,w  ; Agafem la màscara que hem preparat (set 0 i un 1)
  iorwf Sortida,f  ; L'apliquem al vermell
  iorwf Sortida+2,f  ; i al verd, per fer el cursor groc
  comf mascara,w  ; Agafem l'invers de la màscara (set 1 i un 0)
  andwf Sortida+4,f  ; L'apliquem al blau per apagar el LED
; Fi del tractament del cursor
noCur:
  ; A la matriu s'hi envien valors entre 1 i 8 (no de 0 a 7)
  incf caux,w  ; Incrementa l'índex i el guarda a w
  movwf Sortida+1  ; El posa per indicar la filera que escrivim
  movwf Sortida+3  ; El posa per indicar la filera que escrivim
  movwf Sortida+5  ; El posa per indicar la filera que escrivim
  call Envia3max  ; Ho envia al MAX7221
  incf FSR,f  ; Incrementa el punter
  incf caux,f  ; Incrementa l'índex
  decfsz Compt,f  ; Decrementa Compt
  goto BucleMatriu  ; Si Compt no és zero, repeteix el bucle
  movlw 1  ; Retard de 0,771 ms
  call Retms
  decfsz compt_int,f  ; Decrementa el comptador per a la intermitència
  goto Bucle  ; Si no és zero, torna a l'inici del bucle
  movlw cic_int  ; Nombre de cicles per a la intermitència
  movwf compt_int
  incf cur_on,f  ; Incrementa cur_on
  movlw 00000001B  ; Valor 1
  andwf cur_on,f  ; Limitem cur_on entre 0 i 1
  goto Bucle  ; Torna a repetir
;
; Posa a zero els bytes del vector figura
;
PosaZero:
  movlw 8  ; Número de bytes que cal posar a zero
  movwf Compt  ; Variable per comptar els bytes
  movlw figura  ; Agafem l'adreça de la primera posició del vector
  movwf FSR  ; La posem com a punter d'adreçament indirecte
BucleZero:
  clrf INDF  ; Posa a zero la posició actual
  incf FSR,f  ; Incrementa el punter
  decfsz Compt,f  ; Decrementa Compt
  goto BucleZero  ; Si Compt no és zero, repeteix el bucle
  return
;
; Funció que llegeix els polsadors
;
Llegir:
  clrf Polsador  ; Si aquest valor no es canvia és que no hi ha cap polsador premut
  nop  ; espera un microsegon
  nop  ; espera un microsegon
  nop  ; espera un microsegon
  nop  ; espera un microsegon
  nop  ; espera un microsegon, en total 5
  bsf GO_DONE  ; Inicia la conversió
  btfsc GO_DONE  ; Quan el bit sigui 0 la conversió haurà acabat
  goto $-1  ; repetim la línia fins que deixi de ser 0
            ; Comparació P1
  movlw 220  ; Límit superior de P1
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP1  ; ADRESH >= W
  movlw 200  ; Límit inferior de P1
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP1  ; ADRESH < W
  movlw 00000001B  ; Polsador P1
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP1:  ; Comparació P2
  movlw 194  ; Límit superior de P2
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP2  ; ADRESH >= W
  movlw 174  ; Límit inferior de P2
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP2  ; ADRESH < W
  movlw 00000010B  ; Polsador P2
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP2:  ; Comparació P3
  movlw 163  ; Límit superior de P3
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP3  ; ADRESH >= W
  movlw 143  ; Límit inferior de P3
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP3  ; ADRESH < W
  movlw 00000100B  ; Polsador P3
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP3:  ; Comparació P4
  movlw 90  ; Límit superior de P4
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP4  ; ADRESH >= W
  movlw 70  ; Límit inferior de P4
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP4  ; ADRESH < W
  movlw 00001000B  ; Polsador P4
  movwf Polsador  ; Ho copia a Polsador
  goto NoEsP5
NoEsP4:  ; Comparació P5
  movlw 55  ; Límit superior de P5
  subwf ADRESH,w  ; W = ADRESH - W
  btfsc CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP5  ; ADRESH >= W
  movlw 35  ; Límit inferior de P5
  subwf ADRESH,w  ; W = ADRESH - W
  btfss CARRY  ; C = 1 si ADRESH >= W
  goto NoEsP5  ; ADRESH < W
  movlw 00010000B  ; Polsador P5
  movwf Polsador  ; Ho copia a Polsador
NoEsP5:
  return
;
; Apaga tots els LED
;
Apaga:
  movlw 8  ; Hem d'apagar vuit fileres
  movwf Filera  ; Ho guarda al comptador
Repetir:
  movf Filera,w  ; Filera actual
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 00000000B  ; Apagat				
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia3max  ; Ho envia al MAX7221
  decfsz Filera,f  ; Passem a una altra filera
  goto Repetir
  return
;
; Inicialització de tres MAX7221
;
Ini3max:
  movlw 0x0C  ; Shutdown mode
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 0x00  ; Desactivat
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia al MAX7221
  movlw 0x09  ; Decode mode
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 0x00  ; No decode
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia al MAX7221
  movlw 0x0B  ; Scan limit
  movwf Sortida+1  ; Ho prepara per enviar-ho
  movwf Sortida+3  ; Ho prepara per enviar-ho
  movwf Sortida+5  ; Ho prepara per enviar-ho
  movlw 0x07  ; Vuit fileres
  movwf Sortida  ; Ho prepara per enviar-ho
  movwf Sortida+2  ; Ho prepara per enviar-ho
  movwf Sortida+4  ; Ho prepara per enviar-ho
  call Envia_max  ; Ho envia al MAX7221
  return
;
; Envia dades (48 bits) a tres MAX7221
; desactivant les interrupcions
;
Envia3max:
  bcf T0IE  ; Desactiva les interrupcions momentàniament
  call Envia_max
  bsf T0IE  ; Reactiva les interrupcions a l'acabar
  return
;
; Envia dades (48 bits) a tres MAX7221
;
; Els bits estan a les variables:
; Sortida	Valor vermells
; Sortida+1	Adreça vermells
; Sortida+2	Valor verds
; Sortida+3	Adreça verds
; Sortida+4	Valor blaus
; Sortida+5	Adreça blaus
; Al final de la funció, el valor de Sortida queda corromput
; Si es vol conservar, cal copiar-lo a una altra variable
;
; Aquesta funció dura, aproximadament, 1 ms
Envia_max:
  bcf Port,5  ; S'assegura que Clock està desactivat
  bcf Port,6  ; S'assegura que Latch està desactivat
  movf Port,w  ; Agafa el valor de Port
  movwf PORTB  ; I el posa al port B
  movlw 48  ; Número de bits a enviar
  movwf Compta  ; Variable per comptar els bits
Bucle3max:
  bcf Port,4  ; Desactiva Data. Si toca activar-ho, ja ho farem
  rlf Sortida,f  ; Fa sortir el bit de més a l'esquerra cap a C
  rlf Sortida+1,f  ; i roda els altres a l'esquerra
  rlf Sortida+2,f
  rlf Sortida+3,f
  rlf Sortida+4,f
  rlf Sortida+5,f
  btfsc CARRY  ; Mira si el bit de l'esquerra era un 1
  bsf Port,4  ; Si era 1, activa Data
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Data
  movwf PORTB  ; I el posa al port B
  bsf Port,5  ; Activa Clock, forçant a llegir el bit
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Clock
  movwf PORTB  ; I el posa al port B
  bcf Port,5  ; Desactiva Clock
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Clock
  movwf PORTB  ; I el posa al port B
  decfsz Compta,f  ; Decrementa Compta
  goto Bucle3max  ; Si Compta no és zero, repeteix el bucle
  bsf Port,6  ; Torna a activar Latch
              ; Els valors es copiaran a la sortida del registre
  movf Port,w  ; Agafa el valor de Port. El valor que ha canviat és Latch
  movwf PORTB  ; I el posa al port B
  return
;
; Funció de retard de 0,771 W ms
;
Retms:
  movwf Retard2
Buclems:
  decfsz Retard1,f
  goto Buclems
  decfsz Retard2,f
  goto Buclems
  return
END main

 

 

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