Programació en C del PIC 16F690

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

Desenvolupament de jocs senzills

Dinosaure al desert

El joc vol imitar, amb les limitacions que imposa la pantalla, el joc del dinosaure que mostra el navegador Google Chrome quan no es disposa de connexió a internet. El dinosaure es representa amb una columna de tres LED de color vermell, els núvols amb dos LED blaus els cactus una columna de dos LED verds i els pterodàctils dos LED blancs.

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
#include <math.h> 
#include <stdint.h>    // per 'uint8_t'
#define _XTAL_FREQ  4000000    // La freqüència del rellotge és 4 MHz
#define Polsador RA3    // Li assigna un nom a l'adreça del polsador
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];
unsigned char Mort=0;
unsigned char W=0;
unsigned char notes[3][3] = { {189,  95, 0},
                              {212, 106, 2},
                              {238, 119, 2} };
unsigned char tornar_terra;
unsigned char puntuacio_max;    // variable on es guarda la puntuació màxima
unsigned int comptador_punts;    // variable on es guarda la puntuació
char Digits[5];    // Variable amb el nombre a mostrar en pantalla dígit a dígit
char Actiu;    // Variable que diu quin color està actiu
               // Actiu = 0        Apagat
               // Actiu = 1        Vermell
               // Actiu = 2        Verd
               // Actiu = 3        Blau
uint8_t pais[3][8] = { {0b00000000, 0b00000000, 0b10000001, 0b00000000,
                        0b00000000, 0b00000000, 0b00000000, 0b11111111},
                       {0b00000000, 0b00000000, 0b10000001, 0b00000000,
                        0b00000000, 0b00010000, 0b00010000, 0b11111111},
                       {0b01100000, 0b00000110, 0b10000001, 0b00000000,
                        0b00000000, 0b00000000, 0b00000000, 0b00000000} };
unsigned char dino[8] = {0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x00};
// Definició de les funcions que farem servir 
void Envia3max(char Valor[]);
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B);
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
                                           // (filera 1 a 2 i columna 1 a 32, segons pantalla)
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 procesarDatos(unsigned char pais[3][8], unsigned char dino[8], unsigned char Sortida[6], void (*Envia3max)(char[]), unsigned char *Mort,comptador_punts);
void MostrarPuntuacion(unsigned int Valor);
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 = 0b00001000;    // 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();    
    Actiu = 1;    // Activa el color vermell
    TMR0 = 100;    // Presselecció de 100, que són 156 iteracions
    CCP1CON = 0b00001100;
    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
    __delay_ms(2500);    //posem aquest retard perque la pantalla tarda
                         //una mica en engegar i si no deixem temps, no es
                         // mostra el missatge de "PUNTUACIÓ"
    Esborra();    // Esborra la pantalla i posa el cursor a l'inici
    EnviaL('P');    
    EnviaL('U');    
    EnviaL('N');    
    EnviaL('T');    
    EnviaL('U');    
    EnviaL('A');    
    EnviaL('C');    
    EnviaL('I');    
    EnviaL('O');
    EnviaL('=');
    EnviaL('0');    //Escribim "Puntuacio = 0"
    Cursor(2,1);    //Moure el cursor al lloc on desitgem escriure
    EnviaL('M');
    EnviaL('A');
    EnviaL('X');
    Cursor(2,10);
    EnviaL('=');    //Escribim "MAX ="
    INTCON = 0b10100000;    // Activem GIE i T0IE
    Apaga();    // Apaga tots els LED
    while (1) {
        if (Mort==0 && Polsador==1 || tornar_terra == 1){
            //el dinosaure en la seva posició baixa
            dino[2] = 0x00;
            dino[3] = 0x00;
            dino[5] = 0x40;
            dino[6] = 0x40;
            tornar_terra=0;
            comptador_punts=comptador_punts+1;
            Cursor(1, 11);  //Moure el cursor al lloc on desitgem escriure
            MostrarPuntuacion(comptador_punts); //mostrem el valor de la puntuació en temps real
            //es processen les files i es comproba si hem mort
            procesarDatos(pais,dino,Sortida,Envia3max,&Mort,comptador_punts);
        }
        if (Mort==0 && Polsador==0){
            //Polsador ==0 vol dir polsador près
            for (unsigned char e = 0; e < 3; e++){
                comptador_punts=comptador_punts+1;
                Cursor(1, 11); //Moure el cursor al lloc on desitgem escriure
                dino[2] = 0x40;
                dino[3] = 0x40;
                dino[5] = 0x00;
                dino[6] = 0x00;//el dinosaure en la seva posició alta
                MostrarPuntuacion(comptador_punts); //mostrem el valor de la puntuació en temps real
                //es processen les files i es comproba si hem mort
                procesarDatos(pais,dino,Sortida,Envia3max,&Mort,comptador_punts);
            }
            tornar_terra=1;    
        }
        if (Mort==1){
            //Ara mostrarem la puntuació màxima
            if (comptador_punts>puntuacio_max){
                puntuacio_max=comptador_punts;
            }
            Cursor(2, 11); //Moure el cursor al lloc on desitgem escriure
            MostrarPuntuacion(puntuacio_max); //Escribim la puntuació màxima
            comptador_punts=0;
            uint8_t pais[3][8] = { {0b11111111, 0b11111111, 0b11111111, 0b11111111,
                                    0b11111111, 0b11111111, 0b11111111, 0b11111111},
                                   {0b00111100, 0b00011000, 0b10000001, 0b11000011,
                                    0b11000011, 0b10000001, 0b00011000, 0b00111100},
                                   {0b00111100, 0b00011000, 0b10000001, 0b11000011,
                                    0b11000011, 0b10000001, 0b00011000, 0b00111100} }; //pantalla de mort
            for (unsigned char k = 0; k < 8; k++){
                for (unsigned char g=0;g<3;g++){
                    Sortida[2*g+1] = k+1;    //filera
                    Sortida[2*g] = pais[g][k];  // colors
                }
                Envia3max(Sortida);
                __delay_ms(30);
                if (Polsador==0){
                    Mort=0;
                    W = 1; //si hem mort i tornem a clicar, es reinicia el joc
                }
            }
            if (W==0) {
                for (unsigned char v = 0; v<3; v++){
                    TocaNota(notes[v][0],notes[v][1],notes[v][2]);
                } //es toquen les notes do re mi en sentit descendent si morim
                __delay_ms(200);
                W = 2;
            }
            if (W==1) {
                for (unsigned char v = 0; v<3; v++){
                    TocaNota(notes[2-v][0],notes[2-v][1],notes[2-v][2]);
                }//es toquen les notes do re mi en sentit ascendent si tornem a jugar
                __delay_ms(200);
                W = 0;
            }
        }  
    }
}
void __interrupt() temporit(void){

    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 (signed char j = 5; j >= 0; j--){    // Hem d'enviar 6 bytes
        for (signed char 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 {    // D'altra forma
                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 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
    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
}
void Envia_max(void){    // Envia un joc de valors als tres MAX7221
    asm("banksel _Port");
    asm("bcf (_Port&7fh),5");    // S'assegura que Clock està desactivat
    asm("bcf (_Port&7fh),6");    // S'assegura que Latch està desactivat
    asm("movf (_Port&7fh),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&7fh)");    // Variable per comptar els bits
    asm("Bucle:");
    asm("banksel _Port");
    asm("bcf (_Port&7fh),4");    // Desactiva Data. Si toca activar-ho, ja ho farem
    asm("banksel _Sorti");
    asm("rlf (_Sorti&7fh),f");    // Fa sortir el bit de més a l'esquerra cap a C
    asm("rlf ((_Sorti+1)&7fh),f");    // i roda els altres a l'esquerra
    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");    // Mira si el bit de l'esquerra era un 1
    asm("bsf (_Port&7fh),4");    // Si era 1, activa Data
    asm("movf (_Port&7fh),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&7fh),5");    // Activa Clock, forçant a llegir el bit
    asm("movf (_Port&7fh),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&7fh),5");    // Desactiva Clock
    asm("movf (_Port&7fh),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&7fh),f");    // Decrementa Compta
    asm("goto (Bucle&7ffh)");    // Si Compta no és zero, repeteix el bucle
    asm("banksel (_Port&7fh)");
    asm("bsf (_Port&7fh),6");    // Torna a activar Latch
                                                // Els valors es copiaran a la sortida del registre
    asm("movf (_Port&7fh),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 (unsigned char 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 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 procesarDatos(unsigned char pais[3][8], unsigned char dino[8], unsigned char Sortida[6], void (*Envia3max)(char[]), unsigned char *Mort,comptador_punts) {
    for (unsigned char k = 0; k < 8; k++) {
        // Processament inicial
        for (unsigned char g = 0; g < 3; g++) {
            Sortida[2 * g + 1] = k + 1;    // Fila
            Sortida[2 * g] = pais[g][k];    // Colors
        }
        Sortida[0] = pais[0][k] | dino[k];   // Vermells
        // Rotació de bits
        for (unsigned char i = 0; i < 3; i++) {  // Iterem sobre les tres fileres
            pais[i][k] = (pais[i][k] << 1) | (pais[i][k] >> 7);  // Rotació de bits
            if (pais[i][k] == 127) {
                pais[i][k] = 8;  // Restableix a 8
            }
        }
        if (pais[0][k] == 127)
            pais[0][k] = 8;
        // Enviar dades
        Envia3max(Sortida); 
        if (comptador_punts <100){
            for (unsigned char i = 0; i < (5 - round(comptador_punts/10)); i++){
                __delay_ms(0.5);
            } //fem que cada vegada hi hagi menys delay
        } else {
            __delay_ms(2);  // fins arribar a 100 punts, que s'activa la velocitat màxima.
                         // (a la pràctica és més ràpid ja que ens estalviem un bucle)
        }
        // Verificar condició de mort
        if ((pais[1][k] & dino[k]) != 0) {
            *Mort = 1;  // Modifiquem el valor de Mort
        }
    }
}
void MostrarPuntuacion(unsigned int Valor) {
    // Valor = variable a convertir (dos bytes, de 0 a 65535)
    signed int Digits[5]; // Array para emmagatzemar digits
    // Separar els dígits del valor en base 10
    for (signed char j = 0; j < 5; j++) {
        Digits[j] = Valor % 10;
        Valor = Valor / 10;
    }
    // Convertir els dígits a caràcters ASCII
    for (signed char j = 0; j < 5; j++) {
        Digits[j] = Digits[j] + '0';
    }
    // Mostrar els dígits en ordre invers
    for (signed char j = 4; j >= 0; j--) {
        EnviaL(Digits[j]);
    }
}

 

 

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