Programació en C del PIC 16F690

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

Desenvolupament de jocs senzills

Connecta 4

El joc consisteix en deixar caure fitxes des de la part superior de la pantalla per tal que es situïn en la posició més baixa d'aquella columna que encara estigui lliure. L'objectiu és que un dels dos jugadors consegueixi fer una línia de quatre fitxes, en vertical, horitzontal o diagonal. L'ús dels polsadors és el següent:

Polsador Finalitat
1 Desplaçament a l'esquerra
3 Deixar caure la fitxa
5 Reinici

Pantalla del joc

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 <xc.h>  // Carrega el fitxer de funcions necessari per al compilador XC8
#define _XTAL_FREQ  4000000  // La freqüència del rellotge és 4 MHz
#define cic_int  5  // Nombre de cicles per a la intermitència
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, 1: Vermell, 3: Blau
char Polsad;  // Polsador que s'ha premut
char figura[8][8];  // Matriu que guarda l'estat del tauler (0=buit, 1=vermell, 4=blau)
char x = 0;  // Coordenada X del cursor (0 a 7)
char y = 0;  // Coordenada Y del cursor (0 a 7)
char mirar = 1;  // Espera que es deixi anar el polsador
char compt_int = 0;  // Comptador de cicles per a la intermitència
char cur_on = 1;  // Controla l'estat del cursor (ences/apagat)
char color = 1;  // Comencem amb vermell (jugador 1)
char partida_acabada = 0;  // Estat del joc: 0=jugant, 1=acabada
void Envia3max(char Valor[]);  // Envia valors als MAX7221 (per software)
void Envia_max(void);  // Envia valors als MAX7221 (per interrupció/ASM)
void Ini3max(void);  // Inicialitza els xips de la matriu
void Apaga(void);  // Apaga tots els LED
// Funcionament del joc
char Polsador(void);  // Llegeix quin polsador s'ha premut
char Fi_joc(char jugador);  // Comprova si hi ha 4 en ratlla
char TrobaFilaPerCaure(char columna);  // Calcula la gravetat de la fitxa
// Caràcters pantalla LCD
void EnviaL(char Caracter);  // Envia un caràcter a l'LCD (gestionant el conflicte)
void Esborra(void);  // Esborra la pantalla
void Cursor(char Filera, char Columna);  // Mou el cursor de text
void Envia_String(const char* text);  // Envia frases senceres
void MostraGuanyador(char jugador);  // Gestiona el missatge final de victòria
void main (void) {
    OPTION_REG = 0b10000101;  // Configuració de Timer0: Prescaler 64
    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
    ADCON1 = 0b00010000;  // Posa el conversor a 1/8 de la freqüència
    ADCON0 = 0b00001001;  // Activa el conversor A/D connectat a AN2
    TXSTAbits.BRGH = 1;  // Configuració d'alta velocitat
    BAUDCTLbits.BRG16 = 0;  // Paràmetre de velocitat de 8 bits
    SPBRG = 25;  // Velocitat de 9600 baud (@ 4MHz)
    TXSTAbits.SYNC = 0;  // Comunicació asíncrona
    TXSTAbits.TX9 = 0;  // Comunicació de 8 bits
    Ini3max();  // Inicialitza els tres MAX7221
    __delay_ms(2000);  // Retard perque encengui correctament
    Esborra();  // Neteja qualsevol text anterior
    __delay_ms(10);                
    Envia_String("Connecta 4");  // Escriu el títol inicial
    Actiu = 1;  // Activa el color vermell per la interrupció
    TMR0 = 100;  // Presselecció de 100 per al Timer0
    INTCON = 0b10100000;  // Activem GIE  i T0IE 
    Apaga();  // Apaga tots els LED de la matriu
    // Neteja del tauler lògic (bucle)
    for (signed char j = 0; j < 8; j++){
        for (signed char k = 0; k < 8; k++){
            figura[j][k] = 0;  // Posa totes les caselles a 0 (buit)
        }
    }
    x = 0;  // Cursor comença a la dreta
    y = 0;  // Cursor a dalt
    partida_acabada = 0;  // Estat inicial: jugant
    color = 1;  // Comença el jugador Vermell
    // Bucle infinit
    while (1) {
        // a) Joc en marxa
        if (partida_acabada == 0) {
            Polsad = Polsador();  // Llegim els polsadors
            if (mirar == 1) {
                // Polsador 1: Moure Dreta
                if (Polsad == 1) {
                    x = (x + 1) % 8;  // Incrementa x i fa la volta (0-7)
                    mirar = 0;
                }
                // Polsador 3: Deixar caure fitxa
                if (Polsad == 3) {
                    signed char fila_on_caura = TrobaFilaPerCaure(x); // Calcula on cau
                    if (fila_on_caura != -1) {  // Si la columna no està plena
                        figura[fila_on_caura][x] = color; // Guarda la fitxa
                        // Comprovar victòria
                        if (Fi_joc(color) == 1) {
                            partida_acabada = 1;  // Marca el joc com acabat
                            MostraGuanyador(color); // Mostra missatge a l'LCD
                        } else {
                        // Canvi de torn (1->4, 4->1)
                            if (color == 1) color = 4;
                            else color = 1;
                        }
                    }
                    mirar = 0;
                }
            } else {
                if (Polsad == 0) mirar = 1; // Espera que deixis anar el botó
            }
        } else {
            // b) joc acabat
            Polsad = Polsador();
            // Només permetem reiniciar amb P5 quan s'ha acabat
            if (mirar == 1 && Polsad == 5) { 
                 for (signed char j = 0; j < 8; j++) {
                    for (signed char k = 0; k < 8; k++) {
                        figura[j][k] = 0; // Esborra tauler
                    }
                }
                x = 0;
                color = 1;
                partida_acabada = 0;
                mirar = 0;
                Esborra();  // Esborra missatge de victòria
                __delay_ms(10);
                Envia_String("Connecta 4"); // Posa títol de nou
            } else if (Polsad == 0) {
                mirar = 1;
            }
        }
        // fitxes a la matriu de led
        for (signed char j = 0; j < 8; j++){  // Per cada fila
            char mascara;
            char col;
            Sortida[0] = 0;  // Vermells a 0
            Sortida[4] = 0;  // Blaus a 0
            for (signed char k = 0; k < 8; k++){ // Per cada columna
                // Dibuixar cursor (si no ha acabat la partida)
                if (partida_acabada == 0 && (y == j) && (x == k)){ 
                    if (cur_on == 1) col = color; // Cursor parpellejant
                    else col = figura[j][k];  // O el que hi hagi a sota
                } else {
                    col = figura[j][k];  // Color de la fitxa guardada
                }
                // Convertir color (1 o 4) a bits per enviar
                mascara = col & 0b0000001;  // Bit vermell
                Sortida[0] = Sortida[0] | (mascara << k);
                mascara = (col & 0b0000100) >> 2;  // Bit blau
                Sortida[4] = Sortida[4] | (mascara << k);
            }
            Sortida[1] = j+1;  // Selecciona fila vermell
            Sortida[5] = j+1;  // Selecciona fila blau
            Envia3max(Sortida);  // Envia dades als drivers
        }
        __delay_ms(1);  // Petit retard de refresc
        // Control intermitència del cursor
        compt_int++;
        if (compt_int == cic_int){
            compt_int = 0;
            cur_on = (cur_on + 1) % 2; // Inverteix estat cursor
        }
    }
}
void __interrupt() temporit(void){
    if (INTCONbits.T0IF) {  // Si ha saltat el Timer0
        TMR0 = 100;  // Recarrega el Timer
        INTCONbits.T0IF = 0;  // Neteja el flag
        if (Actiu != 0) {
            Actiu--;  // Cicle de colors
            if (Actiu == 0) Actiu = 3;
        }
        Sorti[0] = 0x00;  // Neteja buffers
        Sorti[4] = 0x00;
        if (Actiu == 1) Sorti[0] = 0x01; // Activa vermell
        if (Actiu == 3) Sorti[4] = 0x01; // Activa blau
        Sorti[1] = 0x0C;  // Shutdown mode
        Sorti[5] = 0x0C;  // Shutdown mode
        Envia_max();  // Envia via ASM
    }
}
// funcions matriu de led
void Envia3max(char Valor[]) {
    INTCONbits.T0IE = 0;  // Atura interrupcions 
    char Port = 0;  // Estat del port B
    char Temp;
    for (signed char j = 5; j >= 0; j--){  // 6 bytes a enviar
        for (signed char k = 1; k < 9; k++){ // 8 bits per byte
            Temp = Valor[j] & 0b10000000;
            if (Temp == 0) Port = Port & 0b11101111; // Data = 0
            else Port = Port | 0b00010000;  // Data = 1
            Valor[j] = Valor[j] << 1;
            PORTB = Port;  // Escriu Data
            Port = Port | 0b00100000;  // Clock Alt
            PORTB = Port;
            Port = Port & 0b11011111;  // Clock Baix
            PORTB = Port;
        }
    }
    Port = Port | 0b01000000;  // Activa Latch (bit 6) per copiar a les sortides
    PORTB = Port;  // Ho posa al port B
    INTCONbits.T0IE = 1;  // Reactiva interrupcions a l'acabar
}
void Envia_max(void) {
    asm("banksel _Port");
    asm("bcf (_Port&7fh),5");  // Clock a 0
    asm("bcf (_Port&7fh),6");  // Latch a 0
    asm("movf (_Port&7fh),w");
    asm("banksel PORTB");
    asm("movwf PORTB");
    asm("banksel _Compta");
    asm("movlw 48");  // 48 bits total
    asm("movwf (_Compta&7fh)");
    asm("Bucle:");
    asm("banksel _Port");
    asm("bcf (_Port&7fh),4");  // Data a 0 per defecte
    asm("banksel _Sorti");
    asm("rlf (_Sorti&7fh),f");  // Rota bits...
    asm("rlf ((_Sorti+1)&7fh),f");
    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");  // Si el bit era 1...
    asm("bsf (_Port&7fh),4");  // ...posa Data a 1
    asm("movf (_Port&7fh),w");
    asm("banksel PORTB");
    asm("movwf PORTB");  // Actualitza Port
    asm("banksel _Port");
    asm("bsf (_Port&7fh),5");  // Clock Alt
    asm("movf (_Port&7fh),w");
    asm("banksel PORTB");
    asm("movwf PORTB");
    asm("banksel _Port");
    asm("bcf (_Port&7fh),5");  // Clock Baix
    asm("movf (_Port&7fh),w");
    asm("banksel PORTB");
    asm("movwf PORTB");
    asm("banksel _Compta");
    asm("decfsz (_Compta&7fh),f");  // Decrementa comptador
    asm("goto (Bucle&7ffh)");  // Repeteix
    asm("banksel _Port");
    asm("bsf (_Port&7fh),6");  // Latch Alt
    asm("movf (_Port&7fh),w");
    asm("banksel PORTB");
    asm("movwf PORTB");
}
void Ini3max(void) {
    char Bytes[6];
    Bytes[0] = 0x00;
    Bytes[1] = 0x0C; 
    Bytes[2] = 0x00;
    Bytes[3] = 0x0C;
    Bytes[4] = 0x00; 
    Bytes[5] = 0x0C;
    Envia3max(Bytes);
    Bytes[0] = 0x00;
    Bytes[1] = 0x09;
    Bytes[2] = 0x00;
    Bytes[3] = 0x09; 
    Bytes[4] = 0x00;
    Bytes[5] = 0x09;
    Envia3max(Bytes);
    Bytes[0] = 0x07;
    Bytes[1] = 0x0B; 
    Bytes[2] = 0x07;
    Bytes[3] = 0x0B;
    Bytes[4] = 0x07;
    Bytes[5] = 0x0B;
    Envia3max(Bytes);
}
void Apaga(void) {
    char Bytes[6];
    for (unsigned char j = 1; j <= 8; j++){
        Bytes[1] = j; 
        Bytes[3] = j; 
        Bytes[5] = j;
        Bytes[0] = 0x00;
        Bytes[2] = 0x00;
        Bytes[4] = 0x00;
        Envia3max(Bytes);
    }
}
char TrobaFilaPerCaure(char columna) {
  // Comença des de baix (fila 7) i puja
    for (signed char fila = 7; fila >= 0; fila--) {
        if (figura[fila][columna] == 0) return fila; // Retorna primera buida
    }
    return -1; // Columna plena
}
char Fi_joc(char jugador) {
    char x, y;
    // 1. Comprovació Horizontal
    for (y = 0; y < 8; y++) {
        for (x = 0; x < 5; x++) {
            if (figura[y][x] == jugador &&
                figura[y][x+1] == jugador &&
                figura[y][x+2] == jugador &&
                figura[y][x+3] == jugador) return 1;
        }
    }
    // 2. Comprovació Vertical
    for (x = 0; x < 8; x++) {
        for (y = 0; y < 5; y++) {
            if (figura[y][x] == jugador &&
                figura[y+1][x] == jugador &&
                figura[y+2][x] == jugador &&
                figura[y+3][x] == jugador) return 1;
        }
    }
    // 3. Diagonal Baix-Dreta
    for (y = 0; y < 5; y++) {
        for (x = 0; x < 5; x++) {
            if (figura[y][x] == jugador &&
                figura[y+1][x+1] == jugador &&
                figura[y+2][x+2] == jugador &&
                figura[y+3][x+3] == jugador) return 1;
        }
    }
    // 4. Diagonal Dalt-Dreta
    for (y = 3; y < 8; y++) {
        for (x = 0; x < 5; x++) {
            if (figura[y][x] == jugador &&
                figura[y-1][x+1] == jugador &&
                figura[y-2][x+2] == jugador &&
                figura[y-3][x+3] == jugador) return 1;
        }
    }
    return 0; // Ningú guanya
}
char Polsador(void) {
    char Pols = 0;
    ADCON0bits.GO = 1;  // Posa en marxa el conversor
    while (ADCON0bits.GO == 1);  // Espera que acabi
    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) {
    char estat_int = INTCONbits.GIE;
    INTCONbits.GIE = 0;
    RCSTAbits.SPEN = 1; 
    TXSTAbits.TXEN = 1;
    TXREG = Caracter;
    while (TXSTAbits.TRMT == 0);    
    RCSTAbits.SPEN = 0; 
    TXSTAbits.TXEN = 0;
    INTCONbits.GIE = estat_int;
}
void Esborra(void) {
    EnviaL(254);  // Prefix de control
    EnviaL(1);  // Comanda 1: Esborrar
    __delay_ms(5);  // Espera necessària 
}
void Cursor(char Filera, char Columna) {
    char Posicio = 0;
    if (Filera == 2) Posicio = 64;  // Línia 2 comença a 64
    if (Columna > 0 && Columna < 33) {
        Posicio = Posicio + (Columna - 1);
    }
    Posicio = Posicio + 128;  // Bit de comanda cursor
    EnviaL(254);
    EnviaL(Posicio);
}
void Envia_String(const char* text) {
    for (unsigned char i = 0; text[i] != '\0'; i++) {
        EnviaL(text[i]);  // Envia caràcter a caràcter
    }
}
void MostraGuanyador(char jugador) {
    Esborra();  // Neteja pantalla
    __delay_ms(10);
    if (jugador == 1) Envia_String("Guanya Vermell");
    else Envia_String("Guanya Blau");
    Cursor(2, 1);  // Línia 2
    Envia_String("Reinici BTN5");
}

 

 

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