Programació en C del PIC 16F690

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

Desenvolupament de jocs senzills

Fuego cruzado

El joc treballa amb dues plaques interconnectades, una per a cada jugador. Cada jugador pot disparar projectils i evitar els que llança el contrari. L'ús dels polsadors és el següent:

Polsador Finalitat
0 Disparar
1 Desplaçament amunt
2 Desplaçament avall

En aquest cas, els jugadors tenen assignats els colors blau i vermell. La connexió implementada és una comunicació sèrie asíncrona; de manera que, atès que el cable connecta potes iguals, cal que la pota de recepció sigui diferent en una placa i en l'altra. Aprofitant que cal diferenciar entre els programes de les dues plaques, van optar per fer dos programes diferents; cada un amb les especificitats corresponents a cada jugador. Atès que cada matriu de leds només treballa amb un color, no cal que hi hagi una interrupció que gestioni el canvi de color que es visualitza en cada instant.

El programa del jugador vermell és el següent:

// JUGADOR A (DERECHA) - VERSIÓN FINAL
#pragma config FOSC=INTRCIO, WDTE=OFF, PWRTE=OFF, MCLRE=OFF, CP=OFF, CPD=OFF, BOREN=OFF, IESO=OFF, FCMEN=OFF
#include <xc.h>
#define _XTAL_FREQ 4000000
// --- PINES ---
#define TX_PIN PORTAbits.RA4 // Salida
#define RX_PIN PORTAbits.RA5 // Entrada (Interrupción)
// --- VARIABLES ---
volatile signed char BalaEnemigaX = -1;
volatile signed char BalaEnemigaY = -1;
volatile char CodigoRecibido = 0; // 0=Nada, 1-8=Bala, 100=Gané
signed char JugadorY = 3; // Posición superior del jugador (ocupa Y y Y+1)
signed char BalaMiaX = -1, BalaMiaY = -1;     
char Sortida[6];
int Vidas = 3; 
// --- CARAS PARA GAME OVER ---
const char CARA_FELIZ[8] =  {0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C};
const char CARA_TRISTE[8] = {0x3C, 0x42, 0xA5, 0x81, 0x99, 0xA5, 0x42, 0x3C};
// --- INTERRUPCIÓN DE RECEPCIÓN ---
void __interrupt() ISR(void) {
    if (RABIF) {
        if (RX_PIN == 0) { // Start Bit
            __delay_ms(2);
            if (RX_PIN == 0) {
                __delay_ms(5);
                char dato = 0;
                for(int i=0; i<8; i++) {
                    if(RX_PIN) dato |= (1<<i);
                    __delay_ms(5);
                }
                // INTERPRETACIÓN DEL DATO
                if (dato == 100) {
                    CodigoRecibido = 100; // CÓDIGO DE VICTORIA (El otro murió)
                } else {
                    BalaEnemigaY = (signed char)dato; // Cast explicito para evitar warning
                    BalaEnemigaX = 7;    // Entra por la derecha
                }
            }
        }
        RABIF = 0;
    }
}
// --- ENVÍO ---
void Enviar_Dato(char dato) {
    GIE = 0; // Proteger envío
    TX_PIN = 0; __delay_ms(5);
    for(int i=0; i<8; i++) {
        TX_PIN = (dato >> i) & 1;
        __delay_ms(5);
    }
    TX_PIN = 1; __delay_ms(5);
    GIE = 1;
}
// --- DRIVER PANTALLA ---
void Envia3max(char Valor[]) {
    char Port=0, Temp;
    for(signed char j=5; j>=0; j--) {
        for(signed char k=1; k<9; k++) {
            Temp = Valor[j] & 0b10000000;
            if(Temp==0) Port &= 0b11101111; else Port |= 0b00010000;
            Valor[j] <<= 1; PORTB = Port;
            Port |= 0b00100000; PORTB = Port; 
            Port &= 0b11011111; PORTB = Port; 
        }
    }
    Port |= 0b01000000; PORTB = Port; 
    Port &= 0b10111111; PORTB = Port;
}
void Ini3max(void) {
    char B[6];
    B[0]=1; B[1]=0x0C; B[2]=1; B[3]=0x0C; B[4]=1; B[5]=0x0C; Envia3max(B); 
    B[0]=0; B[1]=0x09; B[2]=0; B[3]=0x09; B[4]=0; B[5]=0x09; Envia3max(B); 
    B[0]=7; B[1]=0x0B; B[2]=7; B[3]=0x0B; B[4]=7; B[5]=0x0B; Envia3max(B); 
}
// --- FUNCIONES VISUALES ---
void Mostrar_Cara(const char* cara, char color_idx) {
    for(int f=0; f<8; f++) {
        Sortida[1]=(char)(f+1); Sortida[3]=(char)(f+1); Sortida[5]=(char)(f+1);
        Sortida[0]=0; Sortida[2]=0; Sortida[4]=0;
        Sortida[color_idx] = cara[f];
        Envia3max(Sortida);
    }
}
void Actualizar_Pantalla(void) {
    for(char f=0; f<8; f++) {
        Sortida[1]=(char)(f+1); Sortida[3]=(char)(f+1); Sortida[5]=(char)(f+1);
        Sortida[0]=0; Sortida[2]=0; Sortida[4]=0;
        
        // JUGADOR (2 PÍXELES DE ALTO)
        if(f==JugadorY || f==(JugadorY+1)) Sortida[0] |= 1;
        // BALAS
        if(BalaMiaX!=-1 && BalaMiaY==f) Sortida[0] |= (1<<BalaMiaX);
        if(BalaEnemigaX!=-1 && BalaEnemigaY==f) Sortida[4] |= (1<<BalaEnemigaX); // Enemigo AZUL
        Envia3max(Sortida);
    }
}
char Leer_Btn(void) {
    ADCON0bits.GO=1; while(ADCON0bits.GO);
    if(ADRESH<220 && ADRESH>200) return 1; 
    if(ADRESH<194 && ADRESH>174) return 2; 
    return 0;
}
void main(void) {
    OSCCONbits.IRCF=0b110; TRISB=0; PORTB=0; TRISC=0; PORTC=0; TRISA=0xFF;
    TRISAbits.TRISA4 = 0; TRISAbits.TRISA5 = 1; PORTAbits.RA4 = 1;
    IOCAbits.IOCA5 = 1; INTCONbits.RABIE = 1; INTCONbits.GIE = 1;
    ANSEL=0b00000100; ANSELH=0; ADCON1=0x10; ADCON0=0x09;
    Ini3max();
    char btn_ant=0, disp_ant=0;
    int tick=0;
    // BUCLE PRINCIPAL (REINICIO AUTOMÁTICO)
    while(1) {
        // --- REINICIO DE VARIABLES ---
        Vidas = 3; BalaMiaX = -1; BalaEnemigaX = -1; CodigoRecibido = 0;
        PORTC = 0b00000111; // 3 Vidas
        // BUCLE DE PARTIDA
        while(Vidas > 0 && CodigoRecibido != 100) {
            // 1. MOVIMIENTO (Límite 6 porque ocupamos 2 píxeles)
            char btn = Leer_Btn();
            if(btn!=btn_ant) {
                if(btn==1 && JugadorY>0) JugadorY--;
                if(btn==2 && JugadorY<6) JugadorY++; // Límite 6
                __delay_ms(20);
            }
            btn_ant = btn;
            // 2. DISPARO INFERIOR (JugadorY + 1)
            if(PORTAbits.RA3==0) {
                if(disp_ant==0 && BalaMiaX==-1) {
                    BalaMiaX=1; BalaMiaY=JugadorY + 1; // Sale del pie
                    Enviar_Dato((char)BalaMiaY); 
                    disp_ant=1;
                }
            } else disp_ant=0;
            // 3. FÍSICA
            tick++;
            if(tick>5) { 
                if(BalaMiaX!=-1) { BalaMiaX++; if(BalaMiaX>7) BalaMiaX=-1; }
                if(BalaEnemigaX!=-1) {
                    BalaEnemigaX--;
                    if(BalaEnemigaX<0) BalaEnemigaX=-1;
                    // COLISIÓN (Con cabeza JugadorY o pies JugadorY+1)
                    if(BalaEnemigaX==0 && (BalaEnemigaY==JugadorY || BalaEnemigaY==JugadorY+1)) {
                        Vidas--;
                        PORTC = 0xFF; __delay_ms(100); PORTC=0; 
                        BalaEnemigaX = -1; 
                        // MOSTRAR VIDAS RESTANTES
                        if(Vidas==2) PORTC=0b00000011;
                        else if(Vidas==1) PORTC=0b00000001;
                    }
                }
                tick=0;
            }
            Actualizar_Pantalla();
        }
        // --- FIN DE PARTIDA ---
        if (Vidas == 0) {
            // PERDÍ: Aviso al otro y muestro cara triste
            Enviar_Dato(100); // Código 100 = "He muerto"
            for(int i=0; i<300; i++) Mostrar_Cara(CARA_TRISTE, 0); // Rojo triste (aprox 3 seg)
        } else if (CodigoRecibido == 100) {
            // GANÉ: El otro me avisó
            for(int i=0; i<300; i++) Mostrar_Cara(CARA_FELIZ, 2); // Verde feliz (aprox 3 seg)
        }
    }
}

El programa del jugador blau és el següent:

// JUGADOR B (IZQUIERDA) - VERSIÓN FINAL    
#pragma config FOSC=INTRCIO, WDTE=OFF, PWRTE=OFF, MCLRE=OFF, CP=OFF, CPD=OFF, BOREN=OFF, IESO=OFF, FCMEN=OFF
#include <xc.h>
#define _XTAL_FREQ 4000000
// --- PINES (INVERTIDOS RESPECTO A PLACA A) ---
#define TX_PIN PORTAbits.RA5 // Salida (B envía por RA5)
#define RX_PIN PORTAbits.RA4 // Entrada (B escucha por RA4)
// --- VARIABLES ---
volatile signed char BalaEnemigaX = -1;
volatile signed char BalaEnemigaY = -1;
volatile char CodigoRecibido = 0;
signed char JugadorY = 3;
signed char BalaMiaX = -1, BalaMiaY = -1;     
char Sortida[6];
int Vidas = 3; 
// --- CARAS ---
const char CARA_FELIZ[8] =  {0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C};
const char CARA_TRISTE[8] = {0x3C, 0x42, 0xA5, 0x81, 0x99, 0xA5, 0x42, 0x3C};
// --- INTERRUPCIÓN (RA4) ---
void __interrupt() ISR(void) {
    if (RABIF) {
        if (RX_PIN == 0) {
            __delay_ms(2);
            if (RX_PIN == 0) {
                __delay_ms(5);
                char dato = 0;
                for(int i=0; i<8; i++) {
                    if(RX_PIN) dato |= (1<<i);
                    __delay_ms(5);
                }
                if (dato == 100) {
                    CodigoRecibido = 100; // GANÉ
                } else {
                    BalaEnemigaY = (signed char)dato; // Corrección de signo
                    BalaEnemigaX = 0;    // Entra por la izquierda
                }
            }
        }
        RABIF = 0;
    }
}
// --- ENVÍO ---
void Enviar_Dato(char dato) {
    GIE = 0;
    TX_PIN = 0; __delay_ms(5);
    for(int i=0; i<8; i++) {
        TX_PIN = (dato >> i) & 1;
        __delay_ms(5);
    }
    TX_PIN = 1; __delay_ms(5);
    GIE = 1;
}
// --- DRIVER PANTALLA ---
void Envia3max(char Valor[]) {
    char Port=0, Temp;
    for(signed char j=5; j>=0; j--) {
        for(signed char k=1; k<9; k++) {
            Temp = Valor[j] & 0b10000000;
            if(Temp==0) Port &= 0b11101111; else Port |= 0b00010000;
            Valor[j] <<= 1; PORTB = Port;
            Port |= 0b00100000; PORTB = Port; 
            Port &= 0b11011111; PORTB = Port; 
        }
    }
    Port |= 0b01000000; PORTB = Port; 
    Port &= 0b10111111; PORTB = Port;
}
void Ini3max(void) {
    char B[6];
    B[0]=1; B[1]=0x0C; B[2]=1; B[3]=0x0C; B[4]=1; B[5]=0x0C; Envia3max(B); 
    B[0]=0; B[1]=0x09; B[2]=0; B[3]=0x09; B[4]=0; B[5]=0x09; Envia3max(B); 
    B[0]=7; B[1]=0x0B; B[2]=7; B[3]=0x0B; B[4]=7; B[5]=0x0B; Envia3max(B); 
}
// --- FUNCIONES VISUALES ---
void Mostrar_Cara(const char* cara, char color_idx) {
    for(int f=0; f<8; f++) {
        Sortida[1]=(char)(f+1); Sortida[3]=(char)(f+1); Sortida[5]=(char)(f+1); // Corrección int->char
        Sortida[0]=0; Sortida[2]=0; Sortida[4]=0;
        Sortida[color_idx] = cara[f];
        Envia3max(Sortida);
    }
}
void Actualizar_Pantalla(void) {
    for(char f=0; f<8; f++) {
        Sortida[1]=(char)(f+1); Sortida[3]=(char)(f+1); Sortida[5]=(char)(f+1); // Corrección int->char
        Sortida[0]=0; Sortida[2]=0; Sortida[4]=0;
        // JUGADOR AZUL (2 PÍXELES)
        if(f==JugadorY || f==(JugadorY+1)) Sortida[4] |= (1<<7);
        // BALAS
        if(BalaMiaX!=-1 && BalaMiaY==f) Sortida[4] |= (1<<BalaMiaX);
        if(BalaEnemigaX!=-1 && BalaEnemigaY==f) Sortida[0] |= (1<<BalaEnemigaX); // Enemigo ROJO
        Envia3max(Sortida);
    }
}
char Leer_Btn(void) {
    ADCON0bits.GO=1; while(ADCON0bits.GO);
    if(ADRESH<220 && ADRESH>200) return 1; 
    if(ADRESH<194 && ADRESH>174) return 2; 
    return 0;
}
void main(void) {
    OSCCONbits.IRCF=0b110; TRISB=0; PORTB=0; TRISC=0; PORTC=0; TRISA=0xFF;
    // CONFIGURACIÓN PINES B (INVERTIDO)
    TRISAbits.TRISA4 = 1; // RA4 ENTRADA
    TRISAbits.TRISA5 = 0; // RA5 SALIDA
    PORTAbits.RA5 = 1;    // Reposo
    // INTERRUPCIONES (IOCA en RA4 para Placa B)
    IOCAbits.IOCA4 = 1;   
    INTCONbits.RABIE = 1; 
    INTCONbits.GIE = 1; 
    ANSEL=0b00000100; ANSELH=0; ADCON1=0x10; ADCON0=0x09;
    Ini3max();
    char btn_ant=0, disp_ant=0;
    int tick=0;
    while(1) {
        // REINICIO
        Vidas = 3; BalaMiaX = -1; BalaEnemigaX = -1; CodigoRecibido = 0;
        PORTC = 0b00000111;
        while(Vidas > 0 && CodigoRecibido != 100) {
            // 1. MOVIMIENTO
            char btn = Leer_Btn();
            if(btn!=btn_ant) {
                if(btn==1 && JugadorY>0) JugadorY--;
                if(btn==2 && JugadorY<6) JugadorY++;
                __delay_ms(20);
            }
            btn_ant = btn;
            // 2. DISPARO
            if(PORTAbits.RA3==0) {
                if(disp_ant==0 && BalaMiaX==-1) {
                    BalaMiaX=6; BalaMiaY=JugadorY + 1; // Sale del pie
                    Enviar_Dato((char)BalaMiaY); // Corrección cast
                    disp_ant=1;
                }
            } else disp_ant=0;
            // 3. FÍSICA
            tick++;
            if(tick>5) { 
                if(BalaMiaX!=-1) { BalaMiaX--; if(BalaMiaX<0) BalaMiaX=-1; }
                if(BalaEnemigaX!=-1) {
                    BalaEnemigaX++;
                    if(BalaEnemigaX>7) BalaEnemigaX=-1;
                    // COLISIÓN
                    if(BalaEnemigaX==7 && (BalaEnemigaY==JugadorY || BalaEnemigaY==JugadorY+1)) {
                        Vidas--;
                        PORTC = 0xFF; __delay_ms(100); PORTC=0; 
                        BalaEnemigaX = -1; 
                        if(Vidas==2) PORTC=0b00000011;
                        else if(Vidas==1) PORTC=0b00000001;
                    }
                }
                tick=0;
            }
            Actualizar_Pantalla();
        }
        // FIN DE PARTIDA
        if (Vidas == 0) {
            Enviar_Dato(100); 
            for(int i=0; i<300; i++) Mostrar_Cara(CARA_TRISTE, 4); // Azul triste
        } else if (CodigoRecibido == 100) {
            for(int i=0; i<300; i++) Mostrar_Cara(CARA_FELIZ, 2); // Verde feliz
        }
    }
}

 

 

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