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 1

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
}

 

 

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