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]);
}
}

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