Programació en mpasm del PIC 16F690

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

La placa de la matriu de vuit per vuit LED

Aquesta és una placa que hem desenvolupat per a fer projectes. En ella hi tenim els mateixos elements que a la placa PICkit 2 i, a més, una pantalla sèrie, un brunzidor piezoelèctric, un conjunt de cinc polsadors connectats a una entrada analògica i una matriu de vuit per vuit LED tricolor que podrem fer servir com ens sembli més convenient.

Placa de la matriu de LED

En la placa, les següents potes del microcontrolador ja tenen utilitat:

Nom Entrada Pota PIC Utilització
VDD 5 V 1 Alimentació
VSS GND 20
ICSPDAT RA0 19 Programació
ICSPCLK RA1 18
VPP RA3 4
T1G RA4 3
AN0 RA0 19 Potenciòmetre
AN2 RA2 17 Cinc polsadors
RA3 RA3 4 Polsador
RA5 RA5 2 Lliure
RB4 RB4 13 Matriu de LED
RB5 RB5 12
RB6 RB6 11
TX RB7 10 Pantalla sèrie
RC0 RC0 16 LED
RC1 RC1 15
RC2 RC2 14
RC3 RC3 7
P1A RC5 5 Brunzidor piezoelèctric
RC4 RC4 6 Lliures
AN8 RC6 8
AN9 RC7 9

Important: Si es fa servir la pantalla sèrie i la matriu de LED en el mateix programa hi pot haver conflictes. Quan s'inicia la comunicació sèrie (activant el bit SPEN) la pota RB5 passa a estar configurada com a entrada (independentment de l'estat de TRISB) i, per tant, la comunicació amb la matriu de LED queda bloquejada. Per evitar el problema cal fer dues coses. Per un costat convé desactivar les interrupcions del timer 0 (per evitar que aquestes enviïn dades a la matriu de LED) i activar la comunicació sèrie abans de cada transmissió. A l'acabar la transmissió desactivarem la comunicació i tornarem a activar les interrupcions. Atès que la comunicació sèrie l'activem cada cop, no l'hem d'activar a la part de configuració del programa, o sigui que hi haurà quatre línies que no posarem perquè ja les posem dins la funció.

; Aquestes dues línies no les posarem a la part de configuració perquè 
; ens bloquejarien la matriu de LED. Ja estan dins la funció EnviaL.
;	bsf		RCSTA,SPEN	; Activa comunicació sèrie
;	bsf		STATUS,RP0	; Tria el banc 1
;	bsf		TXSTA,TXEN 	; Activa comunicació
;	bcf		STATUS,RP0	; Tria el banc 0
	Retard1			; Variable de retard	
					;
					; Enviem caràcters a visualitzar
					;
EnviaL	
	bcf		INTCON,GIE	; Desactiva les interrupcions momentàniament
	bsf		RCSTA,SPEN	; Activa comunicació sèrie
	bsf		STATUS,RP0	; Tria el banc 1
	bsf		TXSTA,TXEN 	; Activa comunicació
	bcf		STATUS,RP0	; Tria el banc 0
	movf		Caracter,w	; Agafa el caràcter
	movwf		TXREG		; L'envia
Ret1
	nop
	decfsz		Retard1,f	; Retard de 1,6 ms
	goto		Ret1
	btfss		PIR1,TXIF	; El registre TXREG ha quedat lliure?
	goto		$-1		; No, doncs esperem
	bcf		RCSTA,SPEN	; Desactiva comunicació sèrie
	bsf		STATUS,RP0	; Tria el banc 1
	bcf		TXSTA,TXEN 	; Desactiva comunicació
	bcf		STATUS,RP0	; Tria el banc 0
	bsf		INTCON,GIE	; Activa les interrupcions
	return				; Tornem al lloc des d'on hem vingut

L'entrada RA3 està habitualment activada i es desactiva quan es prem el polsador.

No és convenient emprar els pins de programació si es preveu alimentar la placa a través del programador.

La pota RA3 en la que està connectat el polsador també és emprada pel programador. Això pot fer que hi hagi conflictes entre ells. A l'exemple PB es comenten les precaucions que cal tenir per a evitar-ho.

Matriu de LED

La matriu de LED conté 64 LED tricolor o sigui que hi ha 192 elements. Seria realment complex fer el connexionat d'aquests elements amb el microcontrolador, fins i tot fent servir registres de desplaçament. Per això el fabricant ha optat per un disseny en el que només cal connectar 32 potes, que ja és prou complicat. La següent imatge mostra com estan disposats els LED i el lloc on queden les potes. Encara que les potes estan a la part inferior, la imatge és vista des de la part superior.

Connexions internes de la matriu de LED

Els LED estan connectats de manera que tots els LED de la mateixa filera tenen el càtode (born negatiu) comú i tots els de la mateixa columna i el mateix color tenen l'ànode (born positiu) comú. La següent imatge ens mostra l'esquema de connexionat en el que s'indica el número de pota que correspon a cada punt de connexió.

Distribució de potes a la matriu de LED

Aquestes matrius no permeten encendre simultàniament tots els LED desitjats ja que per activar un LED cal posar a 1 la seva columna (ànode, positiu) i a 0 la seva filera (càtode, negatiu). La manera de funcionar serà doncs treballar de forma seqüencial, filera per filera o columna per columna.

Els LED tricolor no només ens permeten obtenir els tres colors que incorporen (vermell, blau i verd) sinó alguns colors més. En teoria, combinant aquests tres colors (amb diferents intensitats) podem obtenir quasi infinits colors. Però nosaltres només podem tenir els colors activats o desactivats (sense variar-ne la intensitat) i, per tant, el nombre de possibles colors és reduït. En principi, disposem dels de la taula següent:

Vermell (R) Verd (G) Blau (B) Color
1 0 0 Vermell
0 1 0 Verd
0 0 1 Blau
1 1 0 Groc
1 0 1 Magenta
0 1 1 Cian
1 1 1 Blanc

Alternant de forma ràpida dues o més combinacions podem fer alguns colors intermedis; per exemple:

Vermell (R) Verd (G) Blau (B) Color
1 0 1 Morat
0 0 1

Aquests LED els controlarem amb uns xips especials per controlar blocs de LED, els MAX7221.

El circuit que farem servir és el següent. Només hem representat la part corresponent a la matriu de LED, els polsadors i el brunzidor.

Circuit per a la matriu de LED

Cada un dels tres MAX7221 es connecta a les fileres i a les columnes d'un determinat color. Per tant, hi ha un MAX7221 per a cada un dels tres colors. Per poder controlar tots aquests LED cal fer-ho filera per filera o columna per columna. D'això se n'ocuparà directament el MAX7221 i el microcontrolador no se n'haurà d'ocupar.

A l'hora de funcionar, anirem activant seqüencialment un dels tres xips que s'encarregarà d'encendre els LED corresponents al seu color. Anant prou ràpid com perquè no es noti que els colors s'activen seqüencialment, donarem la sensació de que els tres colors estan actius al mateix temps en els LED que els correspon. Aquesta activació seqüencial dels tres xips sí que serà responsabilitat del microcontrolador. Cal repartir els temps entre els tres xips de manera equilibrada perquè sinó els colors no es combinaran correctament. Per això aquesta tasca l'encomanarem a una imterrupció controlada amb el Timer0

Cada MAX7221 és capaç de memoritzar l'estat dels 64 LED que controla. Això vol dir que nosaltres enviarem als xips el patró del dibuix que volem mostrar un cop i no caldrà tornar-ho a enviar fins que canviem el dibuix. Aquesta és una diferència important respecte als registres de desplaçament als que hauríem hagut d'estar enviant les dades constantment i això ens hauria fet necessari un microcontrolador més ràpid. Per enviar les dades als MAX7221 ho farem en forma sèrie, com amb els registres de desplaçament. A cada xip li haurem d'enviar vuit blocs (un per filera) de vuit bits però ho haurem de fer enviant conjunts de setze bits en els que hi haurà la indicació de la filera i l'estat dels LED d'aquesta filera. Com els xips els connectarem un darrera l'altre (com amb els registres de desplaçament), haurem d'enviar blocs de 48 bits (sis bytes). Atès que només enviem una filera cada cop, només cal enviar aquelles fileres que hagin canviat respecte a la situació anterior.

A l'hora d'enviar els setze bits a un MAX7221, primer hem d'enviar el bit més significatiu (15) i després els següents. Si enviem més de setze bits, els primers que hem enviat passaran cap al xip següent de la cadena. La comunicació es fa mitjançant tres potes dels xips: Data (DIN, 1), Clock (CLK, 13) i Latch (CS, 12). Si CS està desactivat, cada cop que activem CLK es considerarà que el valor a DIN és un nou bit; aquest entrarà i farà rodar tots els que ja estan a dins i els primers que haguem entrat sortiran cap al xip següent. Quan haguem acabat d'enviar-los, activarem CS i cada xip memoritzarà els bits que ha rebut.

Dels setze bits que rep un xip, els bits 12 a 15 no serveixen per a res i nosaltres els posarem sempre a zero. Els bits 8 a 11 són els de control i serveixen per indicar quina filera estem enviant o si enviem valors de configuració. Els bits 0 a 7 són els estats dels LED de la filera o els valors de configuració corresponents. La següent taula indica els valors (en hexadecimal) dels bits de control (8 a 15) segons el que es vol enviar.

Valor Nom Funció Comentaris
0x01 Filera 1
0x02 Filera 2
0x03 Filera 3
0x04 Filera 4
0x05 Filera 5
0x06 Filera 6
0x07 Filera 7
0x08 Filera 8
0x00 No-Op No fer res Serveix quan vols enviar dades a un altre MAX7221 però no en vols enviar a aquest
0x09 Decode mode Conversió per a 7 segments Per a matrius de LED ha d'enviar-se 0x00 (no decode)
0x0B Scan limit Nombre de fileres (des de 0) Nosaltres enviarem 0x07 perquè tenim vuit fileres
0x0C Shutdown mode Activa o desactiva el xip Només n'hi pot haver un d'activat (0x01) cada vegada; la resta desactivats (0x00)

Per tal que no es noti el pampallugueig dels LED cal treballar a una freqüència d'oscil·lació de 25 Hz o superior. Si treballem a 25 Hz vol dir que cada cicle ha de durar un màxim de 40 ms. En cada cicle hem d'activar, seqüencialment, els tres colors, per tant hem de canviar de color cada 13,3 ms o menys. Hem decidit fer-ho, més o menys, cada 10 ms; per això posarem el temporitzador a 1/64 (incrementarà TMR0 cada 64 μs) i farem una interrupció cada 156 iteracions, és a dir que posarem una preselecció de 100 a TMR0.

La funció d'interrupció per al Timer 0 és la que s'encarrega de canviar els colors. El color actual es controla, amb la variable Actiu, segons la següent taula:

Actiu Color
0 Matriu apagada
1 Vermell actiu
2 Verd actiu
3 Blau actiu

Si el valor de la variable Actiu no és zero, a cada interrupció es canvia el color. Si, en canvi, val zero es manté a zero. Per tant, a l'estat de desactivació s'hi entra i se'n surt des del programa principal. La funció d'interrupció activa el xip del color que correspon o cap d'ells.

Atès que a la funció Envia3max se la crida tant des del programa principal com de la interrupció, podríem tenir problemes si la interrupció es produeix mentre la funció s'està executant ja que canviaria l'estat de les variables. Per evitar-ho, a la interrupció podríem guardar tots els valors de les variables que fa servir Envia3max i restituir-los a l'acabar. Però també podem tenir problemes si un color s'activa quan s'està a mig enviar. Per això el que hem fet és desactivar les interrupcions del timer 0 durant l'enviament i així ens estalviem de guardar els valors de les variables.

És molt important que la funció d'interrupció durin el mateix per a cada un dels tres colors. A la taula següent determinem quan dura la funció d'interrupció per a cada color. Hem marcat amb color les instruccions que duren doble. A la funció Activar hem afegit a cada color els nop necessaris.

Color Vermell Verd Blau
Valor inicial Actiu 2 3 1
Valor final Actiu 1 2 3
Instruccions
incrementar
decf Actiu,f
btfss STATUS,Z
goto NoZero
 
decf Actiu,f
btfss STATUS,Z
goto NoZero
 
decf Actiu,f
btfss STATUS,Z
movlw .3
movwf Actiu
Instruccions
activar color
movlw .1
xorwf Actiu,w
btfsc STATUS,Z
goto Vermell
 
 
 
movlw .1
xorwf Actiu,w
btfsc STATUS,Z
movlw .2
xorwf Actiu,w
btfsc STATUS,Z
goto Verd
movlw .1
xorwf Actiu,w
btfsc STATUS,Z
movlw .2
xorwf Actiu,w
btfsc STATUS,Z
 
Durada 9 μs 13 μs 13 μs
Cal afegir 4 μs    

La funció d'interrupció (inclosa la funció Envia3max) dura aproximadament 1 ms. Atès que la interrupció es repeteix cada 7,5 ms vol dir que qualsevol bucle de retard que posem al programa principal es veurà incrementat en un 14 %. L'enviament de les dades a una matriu completa dura uns 8,9 ms.

	cblock 0x20
Port			; Bits a enviar al port B
Sortida:6		; Valors a enviar al MAX7221 (48 bits)
Compta			; Variable per comptar els bits
Filera			; Variable per comptar fileres
Actiu			; Variable que diu quin color està actiu
			; Actiu = 0		Apagat
			; Actiu = 1		Vermell
			; Actiu = 2		Verd
			; Actiu = 3		Blau
	endc
	cblock 0x70	; Zona de memòria de dades que no depèn del banc triat
W_Copia			; Guardarà el contingut de W durant la interrupció
ST_Copia		; Guardarà STATUS durant la interrupció 
	endc
Interrup
	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	INTCON,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	INTCON,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	b'00000011'	; Descarta els bits que no serveixen
	andwf	Actiu,f
	btfsc	STATUS,Z
	goto	NoZero		; Si Actiu val 0 no ho canvia
	decf	Actiu,f		; Passem a activar un altre color
	btfss	STATUS,Z	; 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	b'00000011'	; Descarta els bits que no serveixen
	andwf	Actiu,f
	btfsc	STATUS,Z	; Salta si el resultat no és zero
	goto	Apagat		; Si està apagat ho desactiva tot
	movlw	.1
	xorwf	Actiu,w
	btfsc	STATUS,Z
	goto	Vermell		; Si val 1, activem el vermell
	movlw	.2
	xorwf	Actiu,w
	btfsc	STATUS,Z
	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
				;
				; 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	b'00000000'	; 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	INTCON,T0IE	; Desactiva les interrupcions momentàniament
	call	Envia_max
	bsf	INTCON,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	STATUS,C	; 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,2 W s
				;
Retard				
	movwf	Retard3
Bucles
	decfsz	Retard1,f		
	goto	Bucles		
	decfsz	Retard2,f		
	goto	Bucles		
	decfsz	Retard3,f	
	goto	Bucles
	return
	end

El programa de l'exemple NM permet provar el brunzidor i el programa de l'exemple PA permet provar els polsadors. A l'exemple ML mostrem uns dibuixos estàtics a la matriu de LED. A l'exemple DM dibuixem en un sol color sobre la matriu fent servir els polsadors per moure un cursor i a l'exemple D6 ho fem emprant sis possibles colors.

 

 

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