Programació en pic-as 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 SPEN  ; Activa comunicació sèrie
; bsf RP0  ; Tria el banc 1
; bsf TXEN  ; Activa comunicació
; bcf RP0  ; Tria el banc 0
  Retard1  ; Variable de retard	
;
; Enviem caràcters a visualitzar
;
EnviaL:
  bcf GIE  ; Desactiva les interrupcions momentàniament
  bsf SPEN  ; Activa comunicació sèrie
  bsf RP0  ; Tria el banc 1
  bsf TXEN  ; Activa comunicació
  bcf 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 TXIF  ; El registre TXREG ha quedat lliure?
  goto $-1  ; No, doncs esperem
  bcf SPEN  ; Desactiva comunicació sèrie
  bsf RP0  ; Tria el banc 1
  bcf TXEN  ; Desactiva comunicació
  bcf RP0  ; Tria el banc 0
  bsf 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 ZERO
goto NoZero
 
decf Actiu,f
btfss ZERO
goto NoZero
 
decf Actiu,f
btfss ZERO
movlw 3
movwf Actiu
Instruccions
activar color
movlw 1
xorwf Actiu,w
btfsc ZERO
goto Vermell
 
 
 
movlw 1
xorwf Actiu,w
btfsc ZERO
movlw 2
xorwf Actiu,w
btfsc ZERO
goto Verd
movlw 1
xorwf Actiu,w
btfsc ZERO
movlw 2
xorwf Actiu,w
btfsc ZERO
 
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.

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
Filera EQU 0x28  ; Variable per comptar fileres
Actiu EQU 0x29  ; Variable que diu quin color està actiu
                ; Actiu = 0   Apagat
                ; Actiu = 1   Vermell
                ; Actiu = 2   Verd
                ; Actiu = 3   Blau
; 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ó 
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
...
;
; 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,2 W s
;
Retard:		
  movwf Retard3
Bucles:
  decfsz Retard1,f		
  goto Bucles		
  decfsz Retard2,f		
  goto Bucles		
  decfsz Retard3,f	
  goto Bucles
  return
END main

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.