En aquest cas hi ha un jugador que intenta enfonsar els vaixells que ha situat la màquina. El programa situa sis vaixells triats, de manera aleatòria, entre vuit opcions possibles:
| Mida | Nombre de vaixells |
| 4 caselles | 1 |
| 3 caselles | 1 |
| 2 caselles | 2 |
| 1 casella | 2 |
Els colors dels LED de la matriu representen:
| Color | Significat |
| Apagat | Casella sobre la que no s'ha disparat |
| Blau | Aigua |
| Vermell | Tocat |
| Groc | 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 | Esquerra |
| 2 | Amunt |
| 3 | Disparar |
| 4 | Avall |
| 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 #include <stdlib.h> // Carrega el fitxer on hi ha la funció rand() #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz #define Polsador0 RA3 // Li assigna un nom a l'adreça del polsador
char Sortida[6]; // Valors a enviar al MAX7221 (16 bits)
char Actiu; // Variable que diu quin color està actiu
char Me[8][8]= {{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}};
char numeros[40][1] = {'0','1','2','3','4','5','6','7','8','9',
'0','1','2','3','4','5','6','7','8','9',
'0','1','2','3','4','5','6','7','8','9',
'0','1','2','3','4','5','6','7','8','9'};
char Port;
char Compta;
char f1;
char f2;
char conta;
char xc;
char yc;
char ym;
char R;
char B;
char V;
char Polsador(void);// Funció de lectura dels polsadors
char Polsad;
char tocats;
char alea;
// 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 EnviaL(char Caracter); // Envia un caràcter void Esborra(void); // Esborra la pantalla i posa el cursor a l'inici void TocaNota(char valPre, char valPos, char valPR2);
void main (void) {
_delay(2000000);
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 = 0; // 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
PORTB = 0; // Inicialitza a 0 el port B
Ini3max(); // Inicialitza els tres MAX7221
Actiu = 1;
CCP1CON = 0b00001100;
TMR0 = 100; // Presselecció de 100, que són 156 iteracions
TXSTAbits.BRGH = 1; // Configuració de velocitat
BAUDCTLbits.BRG16 = 0; // Paràmetre de velocitat de 8 bits
SPBRG = 25; // Velocitat de 9600 baud
TXSTAbits.SYNC = 0; // Comunicació asíncrona
TXSTAbits.TX9 = 0; // Comunicació de 8 bits
INTCON = 0b10100000; // Activem GIE i T0IE
ADCON1 = 0b00010000; // Posa el conversor a 1/8 de la freqüència
ADCON0 = 0b00001001; // Activa el conversor A/D connectat a AN2
Esborra();
EnviaL('<'); //la pantalla indica que premem el pulsador
EnviaL('-');
EnviaL('-');
EnviaL('-');
EnviaL('-');
EnviaL('P');
EnviaL('R');
EnviaL('E');
EnviaL('S');
EnviaL('S');
while (Polsador0 == 1) // Mentre el polsador no estigui premut
; // No fa res
while (Polsador0 == 0) // Mentre el polsador estigui premut
;
srand(TMR0); //Utilitzo Tmr0 com a variable per conseguir l'aleatori ja que amb el polsador anterior sempre sera diferent
alea=rand(); //creo el valor aleatori
Apaga();
xc=8;
yc=8;
R=0;
B=0;
V=0;
TocaNota(238, 119, 2); //Melodia inicial
TocaNota(200, 100, 2);
TocaNota(212, 106, 2);
TocaNota(212, 106, 2);
TocaNota(238, 119, 2);
TocaNota(212, 106, 2);
Esborra();
EnviaL('--'); //la pantalla ens indica nova partida
EnviaL('--');
EnviaL('N');
EnviaL('E');
EnviaL('W');
EnviaL(' ');
EnviaL('G');
EnviaL('A');
EnviaL('M');
EnviaL('E');
EnviaL('--');
conta=35;
tocats=0;
if ((0<=alea)&&(alea<=31)){ //Segons el valor aleatori agafo una configuració inicial
Me[1][1]=1;
Me[1][2]=1;
Me[1][6]=1;
Me[3][2]=1;
Me[3][3]=1;
Me[3][4]=1;
Me[5][2]=1;
Me[6][2]=1;
Me[4][6]=1;
Me[5][6]=1;
Me[6][6]=1;
Me[7][6]=1;
Me[7][0]=1;
}
if ((32<=alea)&&(alea<=63)){
Me[0][0]=1;
Me[1][0]=1;
Me[2][0]=1;
Me[3][0]=1;
Me[1][4]=1;
Me[3][2]=1;
Me[3][3]=1;
Me[6][1]=1;
Me[5][4]=1;
Me[6][4]=1;
Me[4][7]=1;
Me[5][7]=1;
Me[6][7]=1;
}
if ((64<=alea)&&(alea<=95)){
Me[2][2]=1;
Me[3][2]=1;
Me[1][5]=1;
Me[5][1]=1;
Me[4][4]=1;
Me[5][4]=1;
Me[3][6]=1;
Me[4][6]=1;
Me[5][6]=1;
Me[7][1]=1;
Me[7][2]=1;
Me[7][3]=1;
Me[7][4]=1;
}
if ((96<=alea)&&(alea<=127)){
Me[1][0]=1;
Me[1][1]=1;
Me[0][3]=1;
Me[1][3]=1;
Me[2][3]=1;
Me[3][3]=1;
Me[1][6]=1;
Me[4][1]=1;
Me[5][1]=1;
Me[3][5]=1;
Me[3][6]=1;
Me[3][7]=1;
Me[5][5]=1;
}
if ((128<=alea)&&(alea<=159)){
Me[0][7]=1;
Me[7][7]=1;
Me[2][1]=1;
Me[3][1]=1;
Me[2][3]=1;
Me[2][4]=1;
Me[2][5]=1;
Me[4][5]=1;
Me[5][5]=1;
Me[6][5]=1;
Me[7][5]=1;
Me[5][2]=1;
Me[5][3]=1;
}
if ((160<=alea)&&(alea<=191)){
Me[1][1]=1;
Me[3][1]=1;
Me[4][1]=1;
Me[5][1]=1;
Me[6][1]=1;
Me[1][4]=1;
Me[2][4]=1;
Me[2][6]=1;
Me[4][3]=1;
Me[4][4]=1;
Me[4][5]=1;
Me[6][6]=1;
Me[7][6]=1;
}
if ((192<=alea)&&(alea<=223)){
Me[0][7]=1;
Me[1][1]=1;
Me[1][2]=1;
Me[1][3]=1;
Me[1][4]=1;
Me[3][1]=1;
Me[3][2]=1;
Me[3][5]=1;
Me[4][5]=1;
Me[6][1]=1;
Me[6][4]=1;
Me[6][5]=1;
Me[6][6]=1;
}
if ((224<=alea)&&(alea<=256)){
Me[7][7]=1;
Me[7][6]=1;
Me[7][5]=1;
Me[7][4]=1;
Me[7][2]=1;
Me[6][2]=1;
Me[5][2]=1;
Me[5][4]=1;
Me[5][5]=1;
Me[5][7]=1;
Me[3][7]=1;
Me[3][6]=1;
Me[3][4]=1;
}
while (1) {
Polsad = Polsador();
if ((Polsad ==5) && (xc>1)) {
Apaga();
xc--;
}
if ((Polsad ==4) && (yc<8)) {
Apaga();
yc++;
}
if ((Polsad ==2) && (yc>1)) {
Apaga();
yc--;
}
if ((Polsad ==1) && (xc<8)) {
Apaga();
xc++;
}
if ((Polsad ==3) && (Me[yc-1][8-xc]==0)) { //DISPAR AIGUA
conta-=1;
TocaNota(238, 119, 2);
TocaNota(212, 106, 2);
Esborra();
EnviaL('-');
EnviaL('W');
EnviaL('A');
EnviaL('T');
EnviaL('E');
EnviaL('R');
EnviaL('-');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL('S');
EnviaL('H');
EnviaL('O');
EnviaL('T');
EnviaL('S');
EnviaL(':');
if (conta<10) {
EnviaL(numeros[conta-1][1]);
}
if ((9<conta) && (conta<20)) {
EnviaL('1');
EnviaL(numeros[conta-1][1]);
}
if ((19<conta) && (conta<30)) {
EnviaL('2');
EnviaL(numeros[conta-1][1]);
}
if (30<=conta) {
EnviaL('3');
EnviaL(numeros[conta-1][1]);
}
Apaga();
Me[yc-1][8-xc]=2;
}
if ((Polsad==3) && (Me[yc-1][8-xc]==1)) { //DISPAR VAIXELL
conta-=1;
tocats+=1;
TocaNota(141, 71, 0);
TocaNota(126, 63, 2);
TocaNota(141, 71, 0);
Esborra();
EnviaL('-');
EnviaL('T');
EnviaL('O');
EnviaL('U');
EnviaL('C');
EnviaL('H');
EnviaL('E');
EnviaL('D');
EnviaL('-');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL('S');
EnviaL('H');
EnviaL('O');
EnviaL('T');
EnviaL('S');
EnviaL(':');
if (conta<10) {
EnviaL(numeros[conta-1][1]);
}
if ((9<conta) && (conta<20)) {
EnviaL('1');
EnviaL(numeros[conta-1][1]);
}
if ((19<conta) && (conta<30)) {
EnviaL('2');
EnviaL(numeros[conta-1][1]);
}
if (30<=conta) {
EnviaL('3');
EnviaL(numeros[conta-1][1]);
}
Apaga();
Me[yc-1][8-xc]=3;
}
if (tocats>=13) { //soroll de partida guanyada
TocaNota(118, 58, 2);
}
if (conta==-1) { //soroll de partida perduda
TocaNota(238, 119, 2);
}
for (char e=1; e<9; e++){
f1=0;
R=0;
V=0;
B=0;
for (char i=0; i<8; i++){
if (Me[e-1][i]==2){
f2=1<<(7-i);
f1=f1|f2;
}
}
B=f1;
f1=0;
for (char i=0; i<8; i++){
if (Me[e-1][i]==3){
f2=1<<(7-i);
f1=f1|f2;
}
}
R=f1;
if (e==yc) {
if (Me[yc-1][8-xc]==3) {
V=1<<(xc-1);
}
if (Me[yc-1][8-xc]==2) {
R+=1<<(xc-1);
V=1<<(xc-1);
B=B-(1<<(xc-1));
}
if ((Me[yc-1][8-xc]!=3)&&(Me[yc-1][8-xc]!=2)) {
R+=1<<(xc-1);
V=1<<(xc-1);
}
}
if (tocats>=13) { //misatge de victoria i matriu verda per victoria
Esborra();
EnviaL('V');
EnviaL('I');
EnviaL('C');
EnviaL('T');
EnviaL('O');
EnviaL('R');
EnviaL('Y');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL('P');
EnviaL('L');
EnviaL('E');
EnviaL('A');
EnviaL('S');
EnviaL('E');
EnviaL(' ');
EnviaL('D');
EnviaL('E');
EnviaL('S');
EnviaL('C');
EnviaL('O');
EnviaL('N');
EnviaL('E');
EnviaL('C');
EnviaL('T');
B=0;
V=0b11111111;
R=0;
}
if (conta==-1) { //misatge de derrota i matriu vermella per derrota
Esborra();
EnviaL('G');
EnviaL('a');
EnviaL('m');
EnviaL('e');
EnviaL(' ');
EnviaL('o');
EnviaL('v');
EnviaL('e');
EnviaL('r');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL(' ');
EnviaL('P');
EnviaL('L');
EnviaL('E');
EnviaL('A');
EnviaL('S');
EnviaL('E');
EnviaL(' ');
EnviaL('D');
EnviaL('E');
EnviaL('S');
EnviaL('C');
EnviaL('O');
EnviaL('N');
EnviaL('E');
EnviaL('C');
EnviaL('T');
B=0;
V=0;
R=0b11111111;
}
Sortida[1]=e;
Sortida[3]=e;
Sortida[5]=e;
Sortida[0]=R;
Sortida[2]=V;
Sortida[4]=B;
Envia3max(Sortida);
}
__delay_ms(10);
}
}
void __interrupt() temporit(void){
// void interrupt temporit(void) { // Línia alternativa
if (INTCONbits.T0IF) { // Comprovem que hi ha interrupció per Timer 0
TMR0 = 140; // 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
Sortida[0] = 0x00; // Vermell
Sortida[2] = 0x00; // Verd
Sortida[4] = 0x00; // Blau
if (Actiu == 1) { // Si és vermell
Sortida[0] = 0x01; // Vermell activat
}
if (Actiu == 2) { // Si és verd
Sortida[2] = 0x01; // Verd activat
}
if (Actiu == 3) { // Si és blau
Sortida[4] = 0x01; // Blau activat
}
Sortida[1] = 0x0C; // Shutdown mode
Sortida[3] = 0x0C; // Shutdown mode
Sortida[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 _Sortida");
asm("rlf _Sortida,f"); // Fa sortir el bit de més a l'esquerra cap a C
asm("rlf _Sortida+1,f"); // i roda els altres a l'esquerra
asm("rlf _Sortida+2,f");
asm("rlf _Sortida+3,f");
asm("rlf _Sortida+4,f");
asm("rlf _Sortida+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 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
}
void Esborra(void) {
EnviaL(254); // Caràcter de control
EnviaL(1); // Esborra la pantalla i posa el cursor a l'inici
}
void Cursor(char Filera, char Columna) {
char Posicio = 0; // Variable per a calcular la posició
if (Filera == 2) {
Posicio = 64; // La primera columna de la segona fila és 64;
}
if (Columna > 0 && Columna < 33) { // Comprovem que sigui un valor raonable
Posicio = Posicio + Columna; // Sumem les adreces
Posicio = Posicio - 1; // Restem 1 perquè numera des de 0
}
Posicio = Posicio + 128; // Posa el bit de posicionat a 1
EnviaL(254); // Control de la posició del cursor
EnviaL(Posicio); // Canvia el cursor de lloc
}
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.