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.

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à dues 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. // RCSTAbits.SPEN = 1; // Activa comunicació sèrie // TXSTAbits.TXEN = 1; // Activa comunicació
void EnviaL(char Caracter) {
INTCONbits.GIE = 0; // Desactiva les interrupcions momentàniament
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
TXREG = Caracter; // Agafa el caràcter i l'envia
__delay_ms(1); // Donem temps
while (PIR1bits.TXIF == 0) // Esperem que s'acabi d'enviar
; // No fem res
RCSTAbits.SPEN = 0; // Desactiva comunicació sèrie
TXSTAbits.TXEN = 0; // Desactiva comunicació
INTCONbits.GIE = 1; // Activa les interrupcions
}
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 CP es comenten les precaucions que cal tenir per a evitar-ho.

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.

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ó.

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.

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.
Les funcions Envia3max i Envia_max són quasi iguals. La primera la farem servir al programa principal i la segona des de la interrupció. Si fessim servir la mateixa funció, podria passar que hi hagués una interrupció mentre s'està executant la funció i llavors s'executarien dues versions de la mateixa funció. L'única diferència entre les dues és que a Envia3max desactivem les interrupcions del timer 0 en començar i les activem a l'acabar.
Interessa que la funció d'interrupció duri el mínim possible, per tal que afecti poc al programa. Per aquest motiu, la funció Envia_max està implementada en assemblador. Tot i això, la seva durada és de 1,36 ms. Com a comparació, la funció Envia3max dura aproximadament 3,8 ms. Atès que la interrupció es repeteix cada 10 ms vol dir que la interrupció dura un 13,6 % temps total i que qualsevol bucle de retard que posem al programa principal durarà 13,6 % més.
char Port; // Gestió del port a la funció Envia_max char Compta; // Comptador de bits a la funció Envia_max char Sortida[6]; // Valors a enviar al MAX7221 (48 bits) char Sorti[6]; // Valors a enviar al MAX7221 des de la interrupció char Actiu; // Variable que diu quin color està actiu // Actiu = 0 Apagat // Actiu = 1 Vermell // Actiu = 2 Verd // Actiu = 3 Blau
// Definició de les funcions que farem servir void Envia3max(char Valor[]); // Envia un joc de valors als tres MAX7221 // desactivant interrupcions void Envia_max(void); // Envia un joc de valors als tres MAX7221 void Ini3max(void); // Inicialitza els tres MAX7221 void Apaga(void); // Apaga tots els LED
void __interrupt() temporit(void){
if (INTCONbits.T0IF) { // Comprovem que hi ha interrupció per Timer 0
TMR0 = 100; // Preselecció de Timer0
INTCONbits.T0IF = 0; // Desactiva el bit que indica interrupció pel Timer0
if (Actiu != 0) { // Si la matriu no està apagada
Actiu--; // Passem a activar un altre color
if (Actiu == 0) { // Si hem arribat a zero
Actiu = 3; // Torna a posar el 3
}
}
// D'entrada els desactivem els tres
Sorti[0] = 0x00; // Vermell
Sorti[2] = 0x00; // Verd
Sorti[4] = 0x00; // Blau
if (Actiu == 1) { // Si és vermell
Sorti[0] = 0x01; // Vermell activat
}
if (Actiu == 2) { // Si és verd
Sorti[2] = 0x01; // Verd activat
}
if (Actiu == 3) { // Si és blau
Sorti[4] = 0x01; // Blau activat
}
Sorti[1] = 0x0C; // Shutdown mode
Sorti[3] = 0x0C; // Shutdown mode
Sorti[5] = 0x0C; // Shutdown mode
Envia_max(); // Ho envia al MAX7221
}
}
void Envia3max(char Valor[]) { // Envia un joc de valors als tres MAX7221
INTCONbits.T0IE = 0; // Desactiva les interrupcions momentàniament
char Port = 0; // Variable on guardem l'estat del port B
char Temp; // Variable temporal
for (signed char j = 5; j >= 0; j--){ // Hem d'enviar 6 bytes
for (signed char k = 1; k < 9; k++){ // De 8 bits
Temp = Valor[j] & 0b10000000; // Agafa el bit de més a l'esquerra
// Temp només podrà valer 0 o 128
if (Temp == 0) { // Si val 0
Port = Port & 0b11101111; // Desactiva Data (bit 4)
} else { // Si val 128
Port = Port | 0b00010000; // Activa Data (bit 4)
}
Valor[j] = (char) (Valor[j] << 1); // Rodem els bits per situar el següent
PORTB = Port; // Ho posa al port B
Port = Port | 0b00100000; // Activa Clock (bit 5) i força lectura
PORTB = Port; // Ho posa al port B
Port = Port & 0b11011111; // Desactiva Clock (bit 5)
PORTB = Port; // Ho posa al port B
}
}
Port = Port | 0b01000000; // Activa Latch (bit 6) per copiar a les sortides
PORTB = Port; // Ho posa al port B
INTCONbits.T0IE = 1; // Reactiva les interrupcions a l'acabar
}
void Envia_max(void) { // Envia un joc de valors als tres MAX7221
asm("banksel _Port");
asm("bcf (_Port&7fh),5"); // S'assegura que Clock està desactivat
asm("bcf (_Port&7fh),6"); // S'assegura que Latch està desactivat
asm("movf (_Port&7fh),w"); // Agafa el valor de Port
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Compta");
asm("movlw 48"); // Número de bits a enviar
asm("movwf (_Compta&7fh)"); // Variable per comptar els bits
asm("Bucle:");
asm("banksel _Port");
asm("bcf (_Port&7fh),4"); // Desactiva Data. Si toca activar-ho, ja ho farem
asm("banksel _Sorti");
asm("rlf (_Sorti&7fh),f"); // Fa sortir el bit de més a l'esquerra cap a C
asm("rlf ((_Sorti+1)&7fh),f"); // i roda els altres a l'esquerra
asm("rlf ((_Sorti+2)&7fh),f");
asm("rlf ((_Sorti+3)&7fh),f");
asm("rlf ((_Sorti+4)&7fh),f");
asm("rlf ((_Sorti+5)&7fh),f");
asm("banksel _Port");
asm("btfsc STATUS,0"); // Mira si el bit de l'esquerra era un 1
asm("bsf (_Port&7fh),4"); // Si era 1, activa Data
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Data
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Port");
asm("bsf (_Port&7fh),5"); // Activa Clock, forçant a llegir el bit
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Port");
asm("bcf (_Port&7fh),5"); // Desactiva Clock
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Compta");
asm("decfsz (_Compta&7fh),f"); // Decrementa Compta
asm("goto (Bucle&7ffh)"); // Si Compta no és zero, repeteix el bucle
asm("banksel _Port");
asm("bsf (_Port&7fh),6"); // Torna a activar Latch
// Els valors es copiaran a la sortida del registre
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Latch
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
}
void Ini3max(void) { // Inicialitza els tres MAX7221
char Bytes[6]; // Els sis bytes que cal enviar
Bytes[0] = 0x00; // Desactivat
Bytes[1] = 0x0C; // Shutdown mode
Bytes[2] = 0x00;
Bytes[3] = 0x0C;
Bytes[4] = 0x00;
Bytes[5] = 0x0C;
Envia3max(Bytes); // Els envia
Bytes[0] = 0x00; // No decode
Bytes[1] = 0x09; // Decode mode
Bytes[2] = 0x00;
Bytes[3] = 0x09;
Bytes[4] = 0x00;
Bytes[5] = 0x09;
Envia3max(Bytes); // Els envia
Bytes[0] = 0x07; // Vuit fileres
Bytes[1] = 0x0B; // Scan limit
Bytes[2] = 0x07;
Bytes[3] = 0x0B;
Bytes[4] = 0x07;
Bytes[5] = 0x0B;
Envia3max(Bytes); // Els envia
}
void Apaga(void) { // Apaga tots els LED
char Bytes[6]; // Els sis bytes que cal enviar
for (signed char j = 1; j <= 8; j++){ // Hem d'enviar 8 fileres
Bytes[1] = j; // Filera
Bytes[3] = j;
Bytes[5] = j;
Bytes[0] = 0x00; // Vermells
Bytes[2] = 0x00; // Verds
Bytes[4] = 0x00; // Blaus
Envia3max(Bytes); // Els envia
}
}
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.
L'enviament d'imatges a la matriu de LED és un procés lent que ens pot fer disminuir la velocitat d'execució en aquells programes que modifiquen sovint la imatge. De la mateixa manera que hem implementat la funció Envia_max en assemblador, ho podríem fer amb Envia3max. A més, normalment preparem els valors que volem enviar a la matriu just abans de cridar la funció i, per tant, podríem fer servir un vector definit com a variable global en lloc de passar un paràmetre a la funció; això faria la funció més senzilla en assemblador i reduiria una mica l'ocupació de memòria.
Així, a continuació tenim una altra versió, en la qual implementem el que s'ha comentat. Els canvis principals són els següents: afegim una nova variable global Comptar que farà de comptador de bits en la nova funció, fem que la funció Envia3max no tingui paràmetres i a les funcions Ini3max i Apaga suprimim la variable local Bytes i treballem amb la variable global Sortida.
Encara que no és l'objectiu principal, aquests canvis impliquen una menor ocupació de memòria. Per exemple, en la darrera versió de l'exemple ML es redueix l'ocupació de la memòria de programa en 51 paraules i la de dades en 13 bytes.
El programa quedaria així.
char Port; // Gestió del port a la funció Envia_max char Compta; // Comptador de bits a la funció Envia_max char Comptar; // Comptador de bits a la funció Envia3max char Sortida[6]; // Valors a enviar al MAX7221 (48 bits) char Sorti[6]; // Valors a enviar al MAX7221 des de la interrupció char Actiu; // Variable que diu quin color està actiu // Actiu = 0 Apagat // Actiu = 1 Vermell // Actiu = 2 Verd // Actiu = 3 Blau
// Definició de les funcions que farem servir void Envia3max(void); // Envia un joc de valors als tres MAX7221 // desactivant interrupcions void Envia_max(void); // Envia un joc de valors als tres MAX7221 void Ini3max(void); // Inicialitza els tres MAX7221 void Apaga(void); // Apaga tots els LED
void __interrupt() temporit(void){
if (INTCONbits.T0IF) { // Comprovem que hi ha interrupció per Timer 0
TMR0 = 100; // Preselecció de Timer0
INTCONbits.T0IF = 0; // Desactiva el bit que indica interrupció pel Timer0
if (Actiu != 0) { // Si la matriu no està apagada
Actiu--; // Passem a activar un altre color
if (Actiu == 0) { // Si hem arribat a zero
Actiu = 3; // Torna a posar el 3
}
}
// D'entrada els desactivem els tres
Sorti[0] = 0x00; // Vermell
Sorti[2] = 0x00; // Verd
Sorti[4] = 0x00; // Blau
if (Actiu == 1) { // Si és vermell
Sorti[0] = 0x01; // Vermell activat
}
if (Actiu == 2) { // Si és verd
Sorti[2] = 0x01; // Verd activat
}
if (Actiu == 3) { // Si és blau
Sorti[4] = 0x01; // Blau activat
}
Sorti[1] = 0x0C; // Shutdown mode
Sorti[3] = 0x0C; // Shutdown mode
Sorti[5] = 0x0C; // Shutdown mode
Envia_max(); // Ho envia al MAX7221
}
}
void Envia3max(void) { // Envia un joc de valors als tres MAX7221
asm("bcf INTCON,5"); // Desactiva les interrupcions momentàniament
asm("banksel _Port");
asm("bcf (_Port&7fh),5"); // S'assegura que Clock està desactivat
asm("bcf (_Port&7fh),6"); // S'assegura que Latch està desactivat
asm("movf (_Port&7fh),w"); // Agafa el valor de Port
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Comptar");
asm("movlw 48"); // Número de bits a enviar
asm("movwf (_Comptar&7fh)"); // Variable per comptar els bits
asm("Bucles:");
asm("banksel _Port");
asm("bcf (_Port&7fh),4"); // Desactiva Data. Si toca activar-ho, ja ho farem
asm("banksel _Sorti");
asm("rlf (_Sortida&7fh),f"); // Fa sortir el bit de més a l'esquerra cap a C
asm("rlf ((_Sortida+1)&7fh),f"); // i roda els altres a l'esquerra
asm("rlf ((_Sortida+2)&7fh),f");
asm("rlf ((_Sortida+3)&7fh),f");
asm("rlf ((_Sortida+4)&7fh),f");
asm("rlf ((_Sortida+5)&7fh),f");
asm("banksel _Port");
asm("btfsc STATUS,0"); // Mira si el bit de l'esquerra era un 1
asm("bsf (_Port&7fh),4"); // Si era 1, activa Data
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Data
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Port");
asm("bsf (_Port&7fh),5"); // Activa Clock, forçant a llegir el bit
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Port");
asm("bcf (_Port&7fh),5"); // Desactiva Clock
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Comptar");
asm("decfsz (_Comptar&7fh),f"); // Decrementa Compta
asm("goto (Bucles&7ffh)"); // Si Compta no és zero, repeteix el bucle
asm("banksel _Port");
asm("bsf (_Port&7fh),6"); // Torna a activar Latch
// Els valors es copiaran a la sortida del registre
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Latch
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("bsf INTCON,5"); // Reactiva les interrupcions a l'acabar
}
void Envia_max(void) { // Envia un joc de valors als tres MAX7221
asm("banksel _Port");
asm("bcf (_Port&7fh),5"); // S'assegura que Clock està desactivat
asm("bcf (_Port&7fh),6"); // S'assegura que Latch està desactivat
asm("movf (_Port&7fh),w"); // Agafa el valor de Port
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Compta");
asm("movlw 48"); // Número de bits a enviar
asm("movwf (_Compta&7fh)"); // Variable per comptar els bits
asm("Bucle:");
asm("banksel _Port");
asm("bcf (_Port&7fh),4"); // Desactiva Data. Si toca activar-ho, ja ho farem
asm("banksel _Sorti");
asm("rlf (_Sorti&7fh),f"); // Fa sortir el bit de més a l'esquerra cap a C
asm("rlf ((_Sorti+1)&7fh),f"); // i roda els altres a l'esquerra
asm("rlf ((_Sorti+2)&7fh),f");
asm("rlf ((_Sorti+3)&7fh),f");
asm("rlf ((_Sorti+4)&7fh),f");
asm("rlf ((_Sorti+5)&7fh),f");
asm("banksel _Port");
asm("btfsc STATUS,0"); // Mira si el bit de l'esquerra era un 1
asm("bsf (_Port&7fh),4"); // Si era 1, activa Data
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Data
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Port");
asm("bsf (_Port&7fh),5"); // Activa Clock, forçant a llegir el bit
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Port");
asm("bcf (_Port&7fh),5"); // Desactiva Clock
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Compta");
asm("decfsz (_Compta&7fh),f"); // Decrementa Compta
asm("goto (Bucle&7ffh)"); // Si Compta no és zero, repeteix el bucle
asm("banksel _Port");
asm("bsf (_Port&7fh),6"); // Torna a activar Latch
// Els valors es copiaran a la sortida del registre
asm("movf (_Port&7fh),w"); // Agafa el valor de Port. El valor que ha canviat és Latch
asm("banksel PORTB");
asm("movwf PORTB"); // I el posa al port B
}
void Ini3max(void) { // Inicialitza els tres MAX7221
Sortida[0] = 0x00; // Desactivat
Sortida[1] = 0x0C; // Shutdown mode
Sortida[2] = 0x00;
Sortida[3] = 0x0C;
Sortida[4] = 0x00;
Sortida[5] = 0x0C;
Envia3max(); // Els envia
Sortida[0] = 0x00; // No decode
Sortida[1] = 0x09; // Decode mode
Sortida[2] = 0x00;
Sortida[3] = 0x09;
Sortida[4] = 0x00;
Sortida[5] = 0x09;
Envia3max(); // Els envia
Sortida[0] = 0x07; // Vuit fileres
Sortida[1] = 0x0B; // Scan limit
Sortida[2] = 0x07;
Sortida[3] = 0x0B;
Sortida[4] = 0x07;
Sortida[5] = 0x0B;
Envia3max(); // Els envia
}
void Apaga(void) { // Apaga tots els LED
for (unsigned char j = 1; j <= 8; j++){ // Hem d'enviar 8 fileres
Sortida[1] = j; // Filera
Sortida[3] = j;
Sortida[5] = j;
Sortida[0] = 0x00; // Vermells
Sortida[2] = 0x00; // Verds
Sortida[4] = 0x00; // Blaus
Envia3max(); // Els envia
}
}

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