Programació en C del PIC 16F690

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

Mastermind electrònic

Programa del grup 3

En aquest cas poden jugar dues persones o una contra la màquina. La matriu de LED es fa servir de manera que les quatre columnes de l'esquerra mostren les combinacions de colors i les quatre de la dreta mostren la correcció de la tirada corresponent. Durant la partida, els polsadors tenen les següents finalitats:

Polsador Funció
1 Groc
2 Cian
3 Magenta
4 Blanc
5 Apagat
0 Reinici

Hi ha dos modes de joc i el polsador 0 permet seleccionar quin dels dos es desitja. Segons el nivell de dificultat els colors de la correcció són:

Color Fàcil Difícil
Verd Encertat color i posició Encertat color i posició
Blau Encertat color però no posició
Vermell Color incorrecte No encertat

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>
#include <xc.h>
#define _XTAL_FREQ  4000000	
#define Polsador6 RA3			// Li assigna un nom a l'adreça del polsador
char Tirada[8][8];
char ntirada;
	// Groc (0), Cian(1), Magenta(2), Blanc(3), Verd(4), Vermell(5), Blau(6)
char bcolors[3][7] = {  { 1, 0, 1, 1, 0, 1, 0},
			{ 1, 1, 0, 1, 1, 0, 0},
			{ 0, 1, 1, 1, 0, 0, 1}};

	// Groc, Cian, Magenta, Blanc					
char colors[4] = {6, 3, 5, 7};  
char mode = 0;
char vsol[4];
char vtirada[4];
char win;
char Sortida[6];				// Valors a enviar al MAX7221 (48 bits)
char Sortida1[6];
char RGB[3];
char Polsador(void);				// Funció de lectura dels polsadors
char Polsad;
char Actiu;					// Variable que diu quin color està actiu
						// Actiu = 0		Apagat
						// Actiu = 1		Vermell
						// Actiu = 2		Verd
						// Actiu = 3		Blau
						// Definició de les funcions que farem servir 
void Envia3max(char Val[]);			// Envia un joc de valors als tres MAX7221 desactivant interrupcions
void Envia_max(char Valor[]);			// Envia un joc de valors als tres MAX7221
void Ini3max(void);				// Inicialitza els tres MAX7221
void Apaga(void);				// Apaga tots els LED
void corregir(void);
void tirar(void);
void pintar(char pos_fila, char pos_color);
void introduir_sol(void);
void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B);
void TocaNota2(char ValPR2, char ValCCPR1L, char ValDC1B);
void TocaNota3(char ValPR2, char ValCCPR1L, char ValDC1B);
void TocaNota4(char ValPR2, char ValCCPR1L, char ValDC1B);
void TocaNota5(char ValPR2, char ValCCPR1L, char ValDC1B);
void TocaNota6(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 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)
	PORTC=0;
	TRISC = 0;				// Tot el port C Ès de sortida
	ADCON1 = 0b00010000;			// Posa el conversor a 1/8 de la freqüència
	ADCON0 = 0b00001001;	
	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
	CCP1CON = 0b00001100;
	PIR1bits.TMR2IF = 0;
	ANSELH = 0;				// Desactiva les altres entrades analògiques
						// Inicialitza a 0 el port C
	PORTB = 0;				// Inicialitza a 0 el port B
	Ini3max();				// Inicialitza els tres MAX7221
	Actiu = 1;				// Activa el color vermell
	TMR0 = 139;				// Presselecció de 139, que són 117 iteracions
						// Correspon a una interrupció cada 7,5 ms
	INTCON = 0b10100000;			// Activem GIE i T0IE
	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
	Esborra();
	Apaga();
	char posar_sol = 0;
	ntirada = 0;
	win = 0; 				// Indica si es guanya la partida o no;
	while (1) {
		if (win == 2) {
			while (Polsador6 == 1) {		// Mentre el polsador no estigui premut
				;
			}
			Apaga();
			posar_sol = 0;
			Esborra();		// Esborra la pantalla i posa el cursor a l'inici
			ntirada = 0;
			win = 0;		// Indica si es guanya la partida o no;
			PORTC = 0;
			if (mode == 0) {
				mode = 1;
				PORTC = 0;
				PORTC = 8;
			} else {
				mode = 0;
				PORTC = 0;
				PORTC = 4;
			}
		}
		if (posar_sol == 0) {
			introduir_sol();
			PORTC=1;
			EnviaL('G');		// Lletra
			EnviaL('O');		// Lletra
			EnviaL('O');		// Lletra
			EnviaL('D');		// Lletra
			EnviaL(' ');		// Lletra
			EnviaL('L');		// Lletra
			EnviaL('U');		// Lletra
			EnviaL('C');		// Lletra
			EnviaL('K');		// Lletra
			posar_sol = 1;
		}
		if ((ntirada > 7) && (win == 0)) {
			ntirada = 0;
		}
		if (win == 0) {
			tirar();
			corregir();
			ntirada++;
			RGB[0]=0;
			RGB[1]=0;
			RGB[2]=0;
		}
		if (win==1) {
			Esborra();		// Esborra la pantalla i posa el cursor a l'inici
			EnviaL('Y');		// Lletra
			EnviaL('O');		// Lletra
			EnviaL('U');		// Lletra
			EnviaL(' ');		// Lletra
			EnviaL('W');		// Lletra
			EnviaL('I');		// Lletra
			EnviaL('N');		// Lletra
			while (Polsador6 == 1) {		// Mentre el polsador no estigui premut
				TocaNota2(133, 67, 0);		// la4  Retard de 0,2 s
				TocaNota2(168, 84, 2);		// fa#4
				TocaNota2(149, 75, 0);		// sol#4
				TocaNota2(133, 67, 0);		// la#4
				TocaNota2(126, 63, 2);		// si4
				TocaNota2(168, 84, 2);		// fa#4
				TocaNota2(149, 75, 0);		// sol#4
				TocaNota2(133, 67, 0);		// la#4
				TocaNota3(118, 59, 2);		// do5  Retard de 0,1 s
				TocaNota3(158, 79, 2);		// sol4
				TocaNota3(141, 71, 0);		// la4
				TocaNota3(126, 63, 2);		// si4
				TocaNota3(118, 59, 2);		// do5
				TocaNota3(158, 79, 2);		// sol4
				TocaNota3(141, 71, 0);		// la4
				TocaNota3(126, 63, 2);		// si4
				TocaNota4(118, 59, 2);		// do5  Retard de 2,0 s
				break;
			}
			win=2;
			
		}
	}
}
void __interrupt() temporit(void) {
	INTCONbits.GIE = 0;			// Desactiva les interrupcions momentàniament
	if (INTCONbits.T0IF) {			// Comprovem que hi ha interrupció per Timer 0
		TMR0 = 139;			// 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
		Sortida1[0] = 0x00;		// Vermell
		Sortida1[2] = 0x00;		// Verd
		Sortida1[4] = 0x00;		// Blau
		if (Actiu == 1) {		// Si és vermell
			Sortida1[0] = 0x01;	// Vermell activat
		}
		if (Actiu == 2) {		// Si és verd
			Sortida1[2] = 0x01;	// Verd activat
		}
		if (Actiu == 3) {		// Si és blau
			Sortida1[4] = 0x01;	// Blau activat
		}
		Sortida1[1] = 0x0C;		// Shutdown mode
		Sortida1[3] = 0x0C;		// Shutdown mode
		Sortida1[5] = 0x0C;		// Shutdown mode
		Envia3max(Sortida1);		// Ho envia al MAX7221
	}
	INTCONbits.GIE = 1;			// Reactiva les interrupcions a l'acabar
}
void pintar(char pos_fila, char pos_color) {
	char r = bcolors[0][pos_color];	 	// poscolor si te r, g o b
	char g = bcolors[1][pos_color];
	char b = bcolors[2][pos_color];
	r = r << pos_fila;
	g = g << pos_fila;			// de cada fila el led que ha de pintar de 0 a 7
	b = b << pos_fila;
	RGB[0] = RGB[0] | r;			// Vermells
	RGB[1] = RGB[1] | g;			// Verds
	RGB[2] = RGB[2] | b;			// Blaus
}
void corregir(void) {
	int nencerts = 0;
	for (int i = 0; i < 4; i++) {
		if (vtirada[i] == vsol[i]) { 	// Posar verd
			pintar (3 - i, 4);
			nencerts++;
		} else { 			// Posar vermell/Blau
			char encert = 0;
			for (int j = 0; j < 4; j++) {
				if (vtirada[i] == vsol[j]) {
					encert = 1;
				}
			}
			if (encert == 0) {	// Posar vermell
				pintar (3 - i, 5);
			} else {  //Posar blau
				if (mode == 0) {		// Posar blau sent 0 facil
					pintar (3 - i, 6);
				} else { 	// Posar vermell sent dificl
					pintar (3 - i, 5);
				}
			}
		}
	}
	Sortida[1] = ntirada+1;			// Filera a pintar
	Sortida[3] = ntirada+1;
	Sortida[5] = ntirada+1;
	Sortida[0] = RGB[0];			// Filera a pintar
	Sortida[2] = RGB[1];
	Sortida[4] = RGB[2];
	Envia3max(Sortida);
	if (nencerts == 4) {
		win = 1;
	}
}
void tirar(void) {
	for (int i = 0; i < 4; i++) {
		char but=0;
		while (but==0) {
			if (Polsador6 == 0){	// Mentre el polsador estigui premut
				win = 2;
				PORTC = 0;
				if (mode == 0) {
					mode = 1;
				} else {
					mode = 0;
				}
				break;
			}
			but = Polsador();
		}
		while (but == 5) {
			but = Polsador();
		}
		if (but < 5) {
			vtirada[i] = colors[but-1];
			pintar (7 - i, but-1);
			Sortida[1] = ntirada+1;	// Filera a pintar
			Sortida[3] = ntirada+1;
			Sortida[5] = ntirada+1;
			Sortida[0] = RGB[0];	// Filera a pintar
			Sortida[2] = RGB[1];
			Sortida[4] = RGB[2];
			Envia3max(Sortida);
		}
		__delay_ms(100);
		while (but!=0) {
			but = Polsador();
		}
	}
}
void introduir_sol(void) {
	char but;
	for (int i = 0; i < 4; i++) {
		but = Polsador();
		while (but==0) {
			but = Polsador();
		}
		while (but == 5) {
			but = Polsador();
		}
		if (but < 5) {
			vsol[i] = colors[but-1];
		}
		__delay_ms(100);
		while (but>0) {
			but = Polsador();
		}
	}
}
char Polsador(void) {
	char Pols = 0;
	ADCON0bits.GO = 1;			// Posa en marxa el conversor
	while (ADCON0bits.GO == 1)		// Mentre no acabi
		;				// ens esperem
	__delay_ms(30);
	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 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
	}
}
void Envia3max(char Val[]) {			// Envia un joc de valors als tres MAX7221
	INTCONbits.GIE = 0;			// Desactiva les interrupcions momentàniament
	Envia_max(Val);
	INTCONbits.GIE = 1;			// Reactiva les interrupcions a l'acabar
}
void Envia_max(char Valor[]) {			// Envia un joc de valors als tres MAX7221
	INTCONbits.GIE = 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.GIE = 1;			// Reactiva les interrupcions a l'acabar
}
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;
	Envia_max(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;
	Envia_max(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;
	Envia_max(Bytes);			// Els envia
}
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(220);			// Sona tants s
	TRISC = 0b00100000;			// Posem RC5 (sortida del PWM) com a entrada
						// O sigui, silenci
	__delay_ms(50);				// Retard de mite s
}
void TocaNota2(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(180);			// Sona tants s
	TRISC = 0b00100000;			// Posem RC5 (sortida del PWM) com a entrada
						// O sigui, silenci
	__delay_ms(90);				// Retard de mite s
}
void TocaNota3(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(100);			// Sona tants s
	TRISC = 0b00100000;			// Posem RC5 (sortida del PWM) com a entrada
						// O sigui, silenci
	__delay_ms(90);				// Retard de mite s
}
void TocaNota4(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(600);			// Sona tants s
	TRISC = 0b00100000;			// Posem RC5 (sortida del PWM) com a entrada
						// O sigui, silenci
	__delay_ms(90);				// Retard de mite s
}
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
}

 

 

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