Programació en C del PIC 16F690

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

Joc d'enfonsar vaixells electrònic

Programa del grup 3

En aquest cas hi ha un jugador que intenta enfonsar els vaixells que ha situat la màquina. El programa situa cinc vaixells de manera aleatòria:

Mida Nombre de vaixells
5 caselles 1
4 caselles 1
3 caselles 1
2 caselles 2

Els colors dels LED de la matriu representen:

Color Significat
Apagat Casella sobre la que no s'ha disparat
Blau Aigua
Verd Tocat
Vermell 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 Avall
3 Amunt
4 Dreta
5 Disparar

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 Polsador   RA3  // Li assigna un nom a l'adreça del polsador
#define _XTAL_FREQ  4000000  // La freqüència del rellotge  4 MHz
char Polsad; // Polsador que s'ha premut
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 (16 bits)
char Sorti[6]; // Valors a enviar al MAX7221 des de la interrupció
char Actiu; // Variable que diu quin color està actiu
// Definició de les funcions que farem servir
char polsador(void); // Funció de lectura dels polsadors
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 // Apaga tots els LED
void cursor(int, int, char[3]);// Funció cursor dels vaixells
void pintar(int, int, char[3]);  // Funció pintar vaixells
void ColocarVaixell(int, char[8]); //Funció que coloca vaixell aleatòriament
void EnviaL(char Caracter);  // Envia un caràcter
void Esborra(void);  // Esborra la pantalla i posa el cursor a l'inici
void Cursor(char Filera, char Columna);  // Posiciona el cursor
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  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;
  CCP1CON = 0b00001100;
  CCPR1L = 49;
  PIR1bits.TMR2IF = 0;
  T2CON = 0b00000011;
  ADCON1 = 0b00010000;  // Posa el conversor a 1/8 de la freqüència
  ADCON0 = 0b00001001;  // Inicialitza a 0 el port B
  __delay_ms(1000);
  Ini3max();  // Inicialitza els tres MAX7221
  Actiu = 1;  // Activa el color vermell
  TMR0 = 139;  // Presselecció de 139, que són 117 iteracions
  // Correspon a una interrupció cada 7,5 ms
  INTCON = 0b10100000;  // Activem GIE i T0IE
  Apaga();// Apaga tots els LED
  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
  RCSTAbits.SPEN = 1;  // Activa comunicació sèrie
  TXSTAbits.TXEN = 1;  // Activa comunicació
  RCSTAbits.SPEN = 0;  // desactiva comunicació sèrie
  TXSTAbits.TXEN = 0;  // desactiva comunicació
  int i = 0;
  int j = 0;
  int tocat = 0;
  int intents = 0;
  char color[3];
  char mascara;
  char mirar = 1;
  // matriu colors (estat actual del joc)
  char matriu[3][8] = { {0b00000000, 0b00000000, 0b00000000, 0b00000000,
                         0b00000000, 0b00000000, 0b00000000, 0b00000000},
                        {0b00000000, 0b00000000, 0b00000000, 0b00000000,
                         0b00000000, 0b00000000, 0b00000000, 0b00000000},
                        {0b00000000, 0b00000000, 0b00000000, 0b00000000,
                         0b00000000, 0b00000000, 0b00000000, 0b00000000} };
  // vaixells amagats
  char vaixells[8] = {0b00000000, 0b00000000, 0b00000000, 0b00000000,
                      0b00000000, 0b00000000, 0b00000000, 0b00000000};
  __delay_ms(500);  // Delay per evitar errors a la pantlla LCD i esborrar
  Esborra();  // Esborra la pantalla i posa el cursor a l'inici
  EnviaL('P');  // Lletra
  EnviaL('R');  // Lletra
  EnviaL('E');  // Lletra
  EnviaL('S');  // Lletra
  EnviaL('S');  // Lletra
  EnviaL(' ');  // Lletra
  EnviaL('0');  // Lletra
  Cursor(2, 1);  // Posició
  EnviaL('6');  // Lletra
  EnviaL(' ');  // Lletra
  EnviaL('T');  // Lletra
  EnviaL('I');  // Lletra
  EnviaL('M');  // Lletra
  EnviaL('E');  // Lletra
  EnviaL('S');  // Lletra
  __delay_ms(3000);  // 3 segons el missatge per indicar que ha de pulsar 6 cops el pulsador 0 
  Esborra();
  // Coloquem el vaixells aleatoris de les mides que volem
  ColocarVaixell(5, vaixells); 
  ColocarVaixell(4, vaixells); 
  ColocarVaixell(3, vaixells); 
  ColocarVaixell(2, vaixells); 
  ColocarVaixell(2, vaixells); 
  __delay_ms(500);  // Delay per evitar errors a la pantlla LCD i esborrar
  Esborra();  // Esborra la pantalla i posa el cursor a l'inici
  EnviaL('G');  // Lletra
  EnviaL('O');  // Lletra
  EnviaL('O');  // Lletra
  EnviaL('D');  // Lletra
  Cursor(2, 1);  // Posició
  EnviaL('L');  // Lletra
  EnviaL('U');  // Lletra
  EnviaL('C');  // Lletra
  EnviaL('K');  // Lletra
  __delay_ms(3000);  // 3 segons el missatge de good luck per començar la partida
  Esborra();
  // Inici del joc, pulsadors
  Apaga();
  while (1) {
    Polsad = polsador(); // Llegim els polsadors
    color[0] = matriu[0][i]; //vermell
    color[1] = matriu[1][i]; //verd
    color[2] = matriu[2][i]; //blau
    pintar(i, j, color);
    if (mirar == 1){
      if (Polsad == 1) {// Si s'ha premut el polsador 1:ESQUERRA
        j = (j+1)%8;
        mirar = 0;
      }
      if (Polsad == 2) { // Si s'ha premut el polsador 2:BAIXA
        i = (i+1)%8;
        mirar = 0;
      }
      if (Polsad == 3) { // Si s'ha premut el polsador 3:PUJA
        i = (i-1)%8;
        mirar = 0;
      }
      if (Polsad == 4) { // Si s'ha premut el polsador 4:DRETA
        j = (j-1)%8;
        mirar = 0;
      }
      if (Polsad == 5) { // Si s'ha premut el polsador 5:CONFIRMAR
        mascara = 0b00000001 << j;
        intents = intents + 1;
        if ((vaixells[i] & mascara) != 0){ //TOCAT
          tocat=tocat + 1;
          matriu[1][i] = matriu[1][i] | mascara; //posem a 1 verd
        }
        if ((vaixells[i] & mascara) == 0){ //AIGUA
          matriu[2][i] = matriu[2][i] | mascara; //posem a 1 blau
        }
        color[0] = matriu[0][i]; //vermell
        color[1] = matriu[1][i]; //verd
        color[2] = matriu[2][i]; //blau
        pintar(i, j, color);
        mirar = 0;
      }
    }else {
      if (Polsad == 0) {  // Si no s'ha premut cap polsador (o dos a la vegada)
        mirar = 1;
      }
    }
    if (intents == 40){
      Apaga();
      Esborra();  // Esborra la pantalla i posa el cursor a l'inici
      EnviaL('G');  // Lletra
      EnviaL('A');  // Lletra
      EnviaL('M');  // Lletra
      EnviaL('E');  // Lletra
      Cursor(2, 1);  // Posició
      EnviaL('O');  // Lletra
      EnviaL('V');  // Lletra
      EnviaL('E');  // Lletra
      EnviaL('R');  // Lletra
      while(1);
    }
    if (tocat == 16){
      Apaga();
      Esborra();  // Esborra la pantalla i posa el cursor a l'inici
      EnviaL('Y');  // Lletra
      EnviaL('O');  // Lletra
      EnviaL('U');  // Lletra
      Cursor(2, 1);  // Posició
      EnviaL('W');  // Lletra
      EnviaL('I');  // Lletra
      EnviaL('N');  // Lletra
      while(1);
    }
    cursor(i, j, color); //Cursor vermell
    _delay(50000); // Retard per permetre la visualització
  }
}
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
  }
}
// FUNCIONS
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 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
  }
}
void cursor(int fila, int col, char color[3]){
  //Cursor: donada una fila i una columna, ilumina el LED de la fila i columna amb un color
  char sortida[6] = {color[0], fila + 1, color[1], fila + 1, color[2], fila + 1};
  char A = 0b00000001 << col;
  sortida[0] = color[0] | A; //si vermell
  sortida[2] = color[1] & ~A; //no verd
  sortida[4] = color[2] & ~A; //no blau
  Envia3max(sortida);
}
void pintar(int fila, int col, char color[3]){
  // Pintar: actualitza la fila segons la matriu del joc.
  char sortida[6] = {color[0], fila + 1, color[1], fila + 1, color[2], fila + 1};
  Envia3max(sortida);
}
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 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 ColocarVaixell(int mida, char vaixells[8]) { //Funció per colocar els vaixells aletoris
  char lliure = 0;
  _delay(200000);  // Retard per evitar problemes al polsador
  while (Polsador == 1)  // Mentre el polsador no estigui premut
    ;  // No fa res
  while (Polsador == 0)  // Mentre el polsador estigui premut
    ;  // No fa res
  srand(TMR0);  // Agafa una llavor
  while(!lliure){
    char random = rand() & 0x00FF; //Agafem els 6 primers bits per les posicions i
    // els altres 2 per la direccio(adalt(vertical) 00, dreta 01(horitzontal)
    char columna = 0b00000001 << ((random & 0b00111000) >> 3);
    char fila = random & 0b00000111;
    // direcció amunt(vertical)
    if((random & 0b11000000) == 0 ) {
      lliure = (fila >= 8 - mida); // cap?
      lliure=lliure & (!(vaixells[fila - (-1 * 0b00000001)] & (max(columna,columna <<1))))
             & (!(vaixells[fila - (-1 * 0b00000001)] & columna)) & (!(vaixells[fila - (-1 * 0b00000001)] 
             & (min(columna,columna  >> 1))));
             //original, mira su fila//fiquem el max per evitar problema columna 1 i8
      for(int i=0; i<mida; i++){ // està ocupat?
        lliure=lliure & (!(vaixells[fila - (i * 0b00000001)] & (max(columna,columna <<1))))
               & (!(vaixells[fila - (i * 0b00000001)] & columna))//original, mira fila
               & (!(vaixells[fila - (i * 0b00000001)] & (min(columna,columna  >> 1))));
               //fiquem el max per evitar problema columna 1 i 8
      }
      lliure=lliure & (!(vaixells[fila - (mida * 0b00000001)] & (max(columna,columna <<1)))) 
             & (!(vaixells[fila - (mida * 0b00000001)] & columna)) & (!(vaixells[fila - (mida * 0b00000001)] 
             & (min(columna,columna  >> 1)))); //fiquem el max per evitar problema columna 1 i 8
      if (lliure){ //si cap
        for(int i=0; i<mida; i++){ //coloquem el vaixell de la mida
          vaixells[fila- (i * 0b00000001)] = vaixells[fila - (i * 0b00000001)] | columna;
        }
      }
    }
    // direcció dreta(horitzontal)
    if((random & 0b11000000) == 64 ) {
      lliure = (columna >= (0b00000001 << (mida - 1))); // cap?
      lliure=lliure & (!(vaixells[min(fila,fila-1)] & (columna << 0b00000001)))& (!(vaixells[fila]
             & (columna << 0b00000001))) & (!(vaixells[max(fila,fila+1)] & (columna << 0b00000001)));
      for(int i=0; i<mida; i++){ // està ocupat?
        lliure=lliure & (!(vaixells[min(fila,fila-1)] & (columna >> (i * 0b00000001))))
               & (!(vaixells[fila] & (columna >> (i * 0b00000001))))& (!(vaixells[max(fila,fila+1)] 
               & (columna >> (i * 0b00000001))));
      }
      lliure=lliure & (!(vaixells[min(fila,fila-1)] & (columna >> (mida * 0b00000001)))) 
             & (!(vaixells[fila] & (columna >> (mida * 0b00000001)))) & (!(vaixells[max(fila,fila+1)] 
             & (columna >> (mida * 0b00000001))));
      if (lliure){ //si cap
        for(int i=0; i<mida; i++){ //fiquem vaixell
          vaixells[fila] = vaixells[fila] | (columna >> (i * 0b00000001));
        }
      }
    }
  }
}

 

 

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