En aquest cas hi ha un jugador que intenta enfonsar els vaixells que ha situat la màquina. La posició dels vaixells està emmagatzemada en el programa, per tant cal modificar el programa perquè apareguin en posicions diferents.
El programa situa cinc vaixells:
| Mida | Nombre de vaixells |
| 4 caselles | 1 |
| 3 caselles | 2 |
| 2 caselles | 2 |
Els colors dels LED de la matriu representen:
| Color | Significat |
| Apagat | Casella sobre la que no s'ha disparat |
| Blau | Aigua |
| Groc | Tocat |
| Vermell | Enfonsat |
| Verd | Posició del cursor |
Els polsadors de la part inferior serveixen per moure el cursor i per disparar sobre la casella actual, segons el següent ordre:
| Polsador | Funció |
| 1 | Disparar |
| 2 | Avall |
| 3 | Amunt |
| 4 | Esquerra |
| 5 | Dreta |
El programa és el següent:
#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF #pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF #include "pic16f690.h" // Carrega el fitxer d'adreces i paràmetres del PIC 16F690 #include <xc.h> // Carrega el fitxer de funcions #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz #define testbit(var, bit) ((var) & (1 <<(bit)))
char Port; // Gestió del port a la funció Envia_max
char Compta; // Comptador de bits a la funció Envia_max
char Polsad;
char Sortida[6]; // Valors a enviar al MAX7221 (16 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
char posicio[8] = {0b00111100, 0b00000000, 0b10000000, 0b10001001,
0b10001001, 0b00000000, 0b00001110, 0b00000000};
char disparats[8] = {0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000000, 0b00000000, 0b00000000, 0b00000000};
char enfonsats[8] = {0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000000, 0b00000000, 0b00000000, 0b00000000};
char f;
char c;
char cursor;
char lon;
// Definició de les funcions que farem servir char Polsador(void); 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 TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B);
void main (void){
OPTION_REG = 0b10000101; // 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)
TRISC = 0b00100000; // Tot el port C és de sortida
TRISB = 0; // Tot el port B és de sortida
TRISA = 0xFF; // Tot el port A és d'entrada
ANSEL = 0b00000101; // Configura AN0 i AN2 com entrada analògica
ANSELH = 0; // Desactiva les altres entrades analògiques
PORTC = 0; // Inicialitza a 0 el port C
CCP1CON = 0b00001100;
CCPR1L = 49;
PIR1bits.TMR2IF = 0; // Desactiva el bit d'interrupció del Timer 2
T2CON = 0b00000011;
PORTB = 0; // Inicialitza a 0 el port B
ADCON1 = 0b00010000; // Posa el conversor a 1/8 de la freqüència
ADCON0 = 0b00001001; // Activa el conversor A/D connectat a AN2
// amb el resultat justificat per l'esquerra
Ini3max(); // Inicialitza els tres MAX7221
Actiu = 1; // Activa el color vermell
TMR0 = 100; // Presselecció de 100, que són 156 iteracions
// Correspon a una interrupció cada 7,5 ms
INTCON = 0b10100000; // Activem GIE i T0IE
Apaga(); // Apaga tots els LED
f = 7;
c = 7;
cursor = 0b00000001;
while (1) {
Polsad = Polsador(); // Llegim els polsadors
if ((Polsad == 5) && (c > 0)) { // Si s'ha premut el polsador 5
c--;
cursor = (1 << c);
__delay_ms(500);
}
if ((Polsad == 5) && (c == 0)){
c = 0;
cursor = (1 << c);
__delay_ms(500);
}
if (Polsad == 4) { // Si s'ha premut el polsador 4
c++;
if (c == 8) {
c = 0;
}
cursor = (1 << c);
__delay_ms(500);
}
if ((Polsad == 3) && (f > 0)) { // Si s'ha premut el polsador 3
f--;
cursor = (1 << c);
__delay_ms(500);
}
if ((Polsad == 3) && (f == 0)){
f = 0;
cursor = (1 << c);
__delay_ms(500);
}
if (Polsad == 2) { // Si s'ha premut el polsador 2
f++;
if (f == 8) {
f = 0;
}
cursor = (1<<c);
__delay_ms(500);
}
if (Polsad == 1) { // Si s'ha premut el polsador 1
disparats[f] = disparats[f] | cursor;
}
// Enfonsar els vaixells:
if(testbit(posicio[f], c) && testbit(disparats[f], c) && Polsad == 1){
if((c>0) && testbit(posicio[f], c - 1) && testbit(disparats[f], c - 1 )){ // c-1
if((c>1) && testbit(posicio[f], c - 2) && testbit(disparats[f], c - 2)){
if((c<7) && testbit(posicio[f], c+1) && testbit(disparats[f], c + 1)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c-1));
enfonsats[f]= enfonsats[f] | (1 << (c-2));
enfonsats[f]= enfonsats[f] | (1 << (c+1));
} else if((c>2) && testbit(posicio[f], c-3) && testbit(disparats[f], c-3)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c-1));
enfonsats[f]= enfonsats[f] | (1 << (c-2));
enfonsats[f]= enfonsats[f] | (1 << (c-3));
} else if((c<7) && testbit(posicio[f], c+1)){
lon = 4; //no enfonsat
} else if((c>2) && testbit(posicio[f], c-3)){
lon = 4; // no enfonsat
} else if(c>1){
TocaNota(189, 95, 0);
lon = 3; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c-1));
enfonsats[f]= enfonsats[f] | (1 << (c-2));
}
} else if ((c>1) && testbit(posicio[f], c - 2)){
lon = 3; //o4 no enfonsat
} else if((c<7) && testbit(posicio[f], c + 1) && testbit(disparats[f], c + 1)){
if((c<6) && testbit(posicio[f], c + 2) && testbit(disparats[f], c + 2)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c-1));
enfonsats[f]= enfonsats[f] | (1 << (c+1));
enfonsats[f]= enfonsats[f] | (1 << (c+2));
} else if((c<6) && testbit(posicio[f], c + 2)){
lon = 4; //no enfonsat
} else if((c<7) && (c>0)){
TocaNota(189, 95, 0);
lon = 3; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c-1));
enfonsats[f]= enfonsats[f] | (1 << (c+1));
}
} else if((c<7) && testbit(posicio[f], c + 1)){
lon = 3; //o 4 no enfonsat
} else if(c>0){
TocaNota(158, 79, 2);
lon = 2; // enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c-1));
}
} else if((c<7) && testbit(posicio[f], c + 1 ) && testbit(disparats[f], c + 1)){ //c+1
if((c<6) && testbit(posicio[f], c + 2) && testbit(disparats[f], c + 2)){
if((c<5) && testbit(posicio[f], c+3) && testbit(disparats[f], c + 3)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c+1));
enfonsats[f]= enfonsats[f] | (1 << (c+2));
enfonsats[f]= enfonsats[f] | (1 << (c+3));
} else if((c<5) && testbit(posicio[f],c+3)){
lon = 4; //no enfonsat
} else if((c>0) && testbit(posicio[f], c-1)){ //afegit
lon = 4; //no enfonsat
} else if(c<6){
TocaNota(189, 95, 0);
lon = 3; //enfonsat
enfonsats[f]= enfonsats[f] | (1 << c);
enfonsats[f]= enfonsats[f] | (1 << (c+1));
enfonsats[f]= enfonsats[f] | (1 << (c+2));
}
} else if((c<6) && testbit(posicio[f], c + 2)){
lon = 3; //o4 no enfonsat
} else if((c>0) && testbit(posicio[f], c-1)){ //afegit
lon = 3; //o4 no enfonsat
} else if(c<7){
TocaNota(158, 79, 2);
lon = 2; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f]= enfonsats[f] | (1<<c+1);
}
}
if((f>0) && testbit(posicio[f - 1], c) && testbit(disparats[f - 1], c)){ //f-1
if((f>1) && testbit(posicio[f - 2], c) && testbit(disparats[f - 2], c)){
if((f<7) && testbit(posicio[f + 1], c) && testbit(disparats[f + 1], c)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f-1]= enfonsats[f-1] | (1<<c);
enfonsats[f-2]= enfonsats[f-2] | (1<<c);
enfonsats[f+1]= enfonsats[f+1] | (1<<c);
} else if((f>2) && testbit(posicio[f - 3], c) && testbit(disparats[f - 3], c)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f-1]= enfonsats[f-1] | (1<<c);
enfonsats[f-2]= enfonsats[f-2] | (1<<c);
enfonsats[f-3]= enfonsats[f-3] | (1<<c);
} else if((f<7) && testbit(posicio[f + 1], c)){
lon = 4; //no enfonsat
} else if((f>2) && testbit(posicio[f - 3], c)){
lon = 4; // no enfonsat
} else if(f>1){
TocaNota(189, 95, 0);
lon = 3; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f-1]= enfonsats[f-1] | (1<<c);
enfonsats[f-2]= enfonsats[f-2] | (1<<c);
}
} else if ((f>1) && testbit(posicio[f - 2], c)){
lon = 3; //o 4 no enfonsat
} else if((f<7) && testbit(posicio[f + 1], c) && testbit(disparats[f + 1], c)){
if((f<6) && testbit(posicio[f + 2], c) && testbit(disparats[f + 2], c)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f-1]= enfonsats[f-1] | (1<<c);
enfonsats[f+1]= enfonsats[f+1] | (1<<c);
enfonsats[f+2]= enfonsats[f+2] | (1<<c);
} else if((f<6) && testbit(posicio[f + 2], c)){
lon = 4; //no enfonsat
} else if((f<7) && (f>0)){
TocaNota(189, 95, 0);
lon = 3; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f-1]= enfonsats[f-1] | (1<<c);
enfonsats[f+1]= enfonsats[f+1] | (1<<c);
}
} else if((f<7) && testbit(posicio[f + 1], c)){
lon = 3; //o 4 no enfonsat
} else{
TocaNota(158, 79, 2);
lon = 2; // enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f-1]= enfonsats[f-1] | (1<<c);
}
} else if((f<7) && testbit(posicio[f + 1], c) && testbit(disparats[f + 1], c)){ //f+1
if((f<6) && testbit(posicio[f + 2], c) && testbit(disparats[f + 2], c)){
if((f<5) && testbit(posicio[f + 3], c) && testbit(disparats[f + 3], c)){
TocaNota(238, 119, 2);
lon = 4; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f+1]= enfonsats[f+1] | (1<<c);
enfonsats[f+2]= enfonsats[f+2] | (1<<c);
enfonsats[f+3]= enfonsats[f+3] | (1<<c);
} else if((f<5) && testbit(posicio[f + 3], c)){
lon = 4; //no enfonsat
} else if((f>0) && testbit(posicio[f-1], c)){ //afegit
lon = 4; //no enfonsat
} else if(f<6){
TocaNota(189, 95, 0);
lon = 3; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f+1]= enfonsats[f+1] | (1<<c);
enfonsats[f+2]= enfonsats[f+2] | (1<<c);
}
} else if((f<6) && testbit(posicio[f + 2], c)){
lon = 3; //no enfonsat
} else if ((f>0) && testbit(posicio[f - 1], c)){ //afegit
lon = 3; //no enfonsat
} else if(f<7){
TocaNota(158, 79, 2);
lon = 2; //enfonsat
enfonsats[f]= enfonsats[f] | (1<<c);
enfonsats[f+1]= enfonsats[f+1] | (1<<c);
}
}
}
for (char y=0; y<8; y++) {
char verd;
char vermell;
char blau;
vermell = posicio[y] & disparats[y];
verd = posicio[y] & disparats[y] & (enfonsats[y] ^ 0b11111111) ;
blau = (posicio[y] ^ 0b11111111) & disparats[y];
if (y == f){
vermell = vermell & (~(1<<c));
verd = verd | (1<<c);
blau = blau & (~(1<<c));
}
Sortida[1] = y + 1; // Filera 1
Sortida[3] = y + 1;
Sortida[5] = y + 1;
Sortida[0] = vermell; // Vermells
Sortida[2] = verd; // Verds
Sortida[4] = blau; // Blaus
Envia3max(Sortida);
}
// Ho envia al MAX7221
}
}
void __interrupt() temporit(void){
// void interrupt temporit(void) { // Línia alternativa
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 (int j = 5; j >= 0; j--){ // Hem d'enviar 6 bytes
for (int 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] = 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,5"); // S'assegura que Clock està desactivat
asm("bcf _Port,6"); // S'assegura que Latch està desactivat
asm("movf _Port,w"); // Agafa el valor de Port
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Compta");
asm("movlw 48"); // Número de bits a enviar
asm("movwf _Compta"); // Variable per comptar els bits
asm("Bucle:");
asm("banksel _Port");
asm("bcf _Port,4"); // Desactiva Data. Si toca activar-ho, ja ho farem
asm("banksel _Sorti");
asm("rlf _Sorti,f"); // Fa sortir el bit de més a l'esquerra cap a C
asm("rlf _Sorti+1,f"); // i roda els altres a l'esquerra
asm("rlf _Sorti+2,f");
asm("rlf _Sorti+3,f");
asm("rlf _Sorti+4,f");
asm("rlf _Sorti+5,f");
asm("banksel _Port");
asm("btfsc STATUS,0"); // Mira si el bit de l'esquerra era un 1
asm("bsf _Port,4"); // Si era 1, activa Data
asm("movf _Port,w"); // Agafa el valor de Port. El valor que ha canviat és Data
asm("movwf PORTB"); // I el posa al port B
asm("bsf _Port,5"); // Activa Clock, forçant a llegir el bit
asm("movf _Port,w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("movwf PORTB"); // I el posa al port B
asm("bcf _Port,5"); // Desactiva Clock
asm("movf _Port,w"); // Agafa el valor de Port. El valor que ha canviat és Clock
asm("movwf PORTB"); // I el posa al port B
asm("banksel _Compta");
asm("decfsz _Compta,f"); // Decrementa Compta
asm("goto Bucle"); // Si Compta no és zero, repeteix el bucle
asm("banksel _Port");
asm("bsf _Port,6"); // Torna a activar Latch
// Els valors es copiaran a la sortida del registre
asm("movf _Port,w"); // Agafa el valor de Port. El valor que ha canviat és Latch
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 (int 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
}
}
char Polsador(void) {
char Pols = 0;
ADCON0bits.GO = 1; // Posa en marxa el conversor
while (ADCON0bits.GO == 1) // Mentre no acabi
; // ens esperem
if (ADRESH < 220 && ADRESH > 200) {
Pols = 1; // Comprova polsador 1
}
if (ADRESH < 194 && ADRESH > 174) {
Pols = 2; // Comprova polsador 2
}
if (ADRESH < 163 && ADRESH > 143) {
Pols = 3; // Comprova polsador 3
}
if (ADRESH < 90 && ADRESH > 70) {
Pols = 4; // Comprova polsador 4
}
if (ADRESH < 55 && ADRESH > 35) {
Pols = 5; // Comprova polsador 5
}
return Pols;
}
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B) {
TRISC = 0b00100000; // Definim com volem les E/S del port C
// RC5 (sortida del PWM), de moment, com a entrada
PR2 = ValPR2; // Carrega PR2
CCP1CON = CCP1CON & 0b11001111; // Posa a zero els bits que corresponen a DC1B
ValDC1B = ValDC1B % 4; // DC1B va de 0 a 3
ValDC1B = ValDC1B * 16; // Desplaça els bits a la posició que els correspon a CCP1CON
CCP1CON = CCP1CON | ValDC1B; // Coloca DC1B al seu lloc
CCPR1L = ValCCPR1L; // Carrega CCPR1L, registre que ens dona l'amplada de tON
PIR1bits.TMR2IF = 0; // Desactiva el bit d'interrupció del Timer 2
T2CON = 0b00000111; // Configura el Timer 2
// bits T2KCPS (bits 1-0) a 11 prescalat de 16
// bit 2 (TMR2ON) a 1, Timer activat
// Postscaler TOUTPS (bits 6-3) no afecten al PWM
while (PIR1bits.TMR2IF == 0) // Espera l'activació del bit d'interrupció del Timer 2
; // Esperem
TRISC = 0b00000000; // Posem RC5 (sortida del PWM) com a sortida
__delay_ms(200); // Retard de 0,2 s
TRISC = 0b00100000; // Posem RC5 (sortida del PWM) com a entrada
// O sigui, silenci
__delay_ms(200); // Retard de 0,2 s
}

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