En aquest cas el marcador anava destinat al futbol. La funció dels LED és indicar els modes:
| LED | Significat |
| 3 | S'està jugant la segona part |
| 4 | S'està jugant la primera part |
El potenciòmetre permet passar d'un mode a l'altre. La funció dels polsadors és la següent:
| Polsador | Funció |
| 0 | Reinicia el marcador |
| 1 | Afegeix un gol a l'equip local |
| 2 | Afegeix un gol a l'equip visitant |
| 3 | Sense ús |
| 4 | Canvia de part |
| 5 | Inicia o atura el temps |
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> // Carrega el fitxer d'adreces i paràmetres del PIC 16F690 #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz #define Boto RA3 // Li assigna un nom a l'adreça del polsador #define FiTimer0 INTCONbits.T0IF // Li assigna un nom al bit que indica el final del Timer 0 #define Limit_Descarta 17 // Limit postescalador (Originalment 20 - Sense Interupcions 17) #define SensPot 5 // Sensibilitat del Potenciòmetre (menys -> +sensible) #define V1 (char)(Limit_Descarta/4) #define V2 (char)(2*Limit_Descarta/4) #define V3 (char)(3*Limit_Descarta/4) // a=target variable, b=bit number to act upon 0-n (Per a modificar un sol bit d'un byte) #define BIT_SET(a,b) ((a) |= (1<<(b))) #define BIT_CLEAR(a,b) ((a) &= ~(1<<(b))) #define BIT_FLIP(a,b) ((a) ^= (1<<(b))) #define BIT_CHECK(a,b) (!!((a) & (1<<(b))))
char Digits[5]; // Variable del vector de caràcters unsigned char Part; // Part del partit unsigned char Gol_L; // Gols Local unsigned char Gol_V; // Gols Visitant char Polsad; // Resultat de Polsador() unsigned int Time; // Temps en s char Tex; // Comptador temps extra 1 -> actiu | 0 -> inactiu char mode; // Temps: 0 -> estatic | 1 -> creixent | 2 -> decreixent char Busy; // 0 es pot processar acció, 1 esperant a que es deixi de premer el boto char Descarta; // Variable del postescalador char ExLim; // Temps Extra Màxim char Pot; // Últim valor del potènciometre
// Definició de les funcions que farem servir void EnviaL(char Caracter); // Envia un caràcter void EnviaV(char L1, char L2, char L3, char L4, char L5); // Envia 5 caràcters 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) void DefCarac(char Numero, char Fileres[8]); // Crea un caràcter definit per l'usuari char Polsador(void); // Retorna el polsador premut 1 a 5 (0 si no n'hi ha cap) void TimeUpdate(void); // Actualitza el temps mostrat a la pantalla void TocaNota(char ValPR2, char ValCCPR1L, char ValDC1B); // Toca un sò amb el brunzidor void Input(char Val); // Processa la interacció amb els botons void ResetToPart(void); // Reseteja els comptadors per al canvi de part void Reset(void); // Reseteja tots els comptadors void CheckPot(void); // Mira si ha canviat la posició del potenciòmetre
void main (void) {
// I/O SETUP
TRISC = 0; // Posa els LEDs del port C com a sortida
TRISB = 0; // Tot el port B és de sortida
TRISA = 0xFF; // Posa el boto del port A com a entrada
ANSEL = 0b00000101; // Configura AN0 i AN2 com entrada analògica
ANSELH = 0; // Desactiva les altres entrades analògiques
ADCON1 = 0b00010000;
ADCON0 = 0b00001001; // Activa el conversor A/D connectat a AN2
// TIMER SETUP
OPTION_REG = 0b10000111; // Configuració de Timer0
FiTimer0 = 0; // Aquest bit es posarà a 1 quan el temporitzador acabi
TMR0 = 61;
Descarta = 0; // Variable del postescalador a 0
// PWM SETUP
CCP1CON = 0b00001100; // Configura el PWM, bits P1M (bits 7-6) a 00 mode senzill
// DC1B = 11 (bits 5-4) els dos bits de menys pes són 0
// CCP1M = 11xx en mode senzill els bit 0 i 1 no afecten
// Ho posa com a configuració del PWM
CCPR1L = 49; // Valor que correspon a un cicle del 35 % a 440 Hz
// Registre que ens dona l'amplada de tON
PIR1bits.TMR2IF = 0; // Desactiva el bit d'interrupció del Timer 2
T2CON = 0b00000011; // Configura el Timer 2
// bits T2KCPS (bits 1-0) a 11 prescalat de 16
// bit 2 (TMR2ON) a 0, Timer aturat
// Postscaler TOUTPS (bits 6-3) no afecten al PWM
// LCD SETUP
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
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
_delay(20);
Reset(); // Posem tots els comptadors a 0 i la pantalla en el estat inicial
// MAIN LOOP
while(1){
if (Boto == 0)
Reset();
if(Descarta == V1 || Descarta == V2 || Descarta == V3 || Descarta == Limit_Descarta || mode == 0){
// Es mira els botons 4 vegades per segon o a cada cicle si el temps està parat
Polsad = Polsador(); // Mira si hi ha algun polsador premut
Input(Polsad); // Calcula l'acció corresponent
}
if (Time == 0 && mode == 2) // Si s'ha acabat el compte enrere para el temps (per als finals dels descansos)
mode = 0;
if (FiTimer0 == 1)
CheckPot();
if(mode != 0){
FiTimer0 = 0;
TMR0 = 61;
if(Descarta == Limit_Descarta){ // Post escalador
TimeUpdate();
Descarta = 0;
} else
++Descarta;
}
}
_delay(20);
}
void Input(char Val){
if (Busy == 0){ // Comprovem que no s'estigui processant cap acció anterior
if(Val == 1){ // Cas Polsador 1
Busy = 1;
TocaNota(212, 106, 2); // Nota Re
++Gol_L; // Suma gol a l'equip local
if (Gol_L == 10) // Si hi ha 10 gols o més mostra només la unitat (molt poc probable)
Gol_L = 0;
Cursor(2,3); // Actualitza la pantalla
EnviaL(Gol_L+'0');
} else if (Val == 2){ // Cas Polsador 2
Busy = 1;
TocaNota(189, 95, 0); // Nota Mi
++Gol_V;
if (Gol_V == 10)
Gol_V = 0;
Cursor(2,7);
EnviaL(Gol_V+'0');
} else if (Val == 3){ // Cas Polsador 3
Busy = 1;
Time = 2690; // Posa els temps a uns segons del temps extra (per a depuració)
TimeUpdate();
} else if (Val == 4){ // Cas Polsador 4
Busy = 1;
TocaNota(126, 63, 2); // Nota Si
_delay(10);
TocaNota(126, 63, 2); // Nota Si
if (mode != 2){ // Si estem amb el temps actiu i creixent
if (Part == 1){ // Si estem a la 1a part
PORTC = 0b00000100;
Part = 2;
} else { // Si estem a la 2a part
PORTC = 0b00001000;
Part = 1;
}
Time = 900;// Time = 900s = 15'
mode = 2; // Compte enrere descans
ResetToPart();
} else if (mode == 2) // Si estem en temps de descans acaba'l
Time = 1;
} else if (Val == 5){ // Cas Polsador 5 -- Depuració
Busy = 1;
TocaNota(158, 79, 0); // Nota Sol
if (mode == 1) // Alternar temps avança - aturat
mode = 0;
else if (mode == 0)
mode = 1;
}
} else if (Val == 0){
Busy = 0;
}
}
void ResetToPart(void){
Tex = 0;
Cursor(1,3);
EnviaV('L',' ','-',' ','V');
Cursor(1,10);
EnviaV('0','0','.','0','0');
Cursor(2,9);
EnviaV(' ',' ',' ',' ',' ');
EnviaL(' ');
_delay(1000000);
// WAIT START
FiTimer0 = 0; // Reiniciem el temporitzador
TMR0 = 61;
Descarta = 0;
Polsad = 0;
}
void Reset(void){
Esborra();
// INITIAL CONF
PORTC = 0b00001000; // Configuracio inicial dels LEDs
Part = 1; // Configuracio inicial Part
Gol_L = 0; // Configuracio inicial gols locals
Gol_V = 0; // Configuracio inicial gols visitant
Busy = 0; // Pot processar una acció
mode = 0; // Configuracio inicial Temps (parat)
Cursor(2,3);
EnviaV('0',' ',':',' ','0');
Time = 0;
ResetToPart();
}
// Funcions Pantalla
void EnviaL(char Caracter) {
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
}
void EnviaV(char L1, char L2, char L3, char L4, char L5) {
char Vector[5] = {L1, L2, L3, L4, L5};
for (char j = 0; j < 5; j++){
TXREG = Vector[j]; // 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
}
}
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
}
char Polsador(void) {
char Pols = 0;
ADCON0bits.GO = 1; // Posa en marxa el conversor
_delay(10);
while (ADCON0bits.GO == 1) // Mentre no acabi
; // ens esperem
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 TimeUpdate(void){
if (mode == 0) // Temps congelat
return;
else if (mode == 1){ // Temps avança
Time++;
if(Time == 2701){
Tex = 1;
Time = 1;
Cursor(2,12);
EnviaL('.');
Cursor(2,9);
EnviaL('+');
}
} else if (mode == 2) // Temps retrocedeix
Time--;
// LCDUpdate
if(Tex == 0){
Cursor(1,10);
} else{
Cursor(2,10);
if((ExLim != 0)&&(Time >= ExLim*60)){
Busy = 1;
TocaNota(126, 63, 2); // Nota Si
_delay(10);
TocaNota(126, 63, 2); // Nota Si
if (Part == 1){ // Si estem a la 1a part
PORTC = 0b00000100;
Part = 2;
} else { // Si estem a la 2a part
PORTC = 0b00001000;
Part = 1;
}
Time = 900; // Time = 900s = 15'
mode = 2; // Compte enrere descans
ResetToPart();
Cursor(1,10);
}
}
char Min = Time/60;
char Sec = Time%60;
EnviaV('0' + Min/10, '0' + Min%10, '.', '0' + Sec/10, '0' + Sec%10);
}
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(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 CheckPot(void){
ADCON0 = 0b00000001; // Posa l'ADC a la pota del potenciòmetre
ADCON0bits.GO = 1; // Posa en marxa el conversor
while (ADCON0bits.GO == 1) // Mentre no acabi
; // ens esperem
if (ADRESH > 100)
ExLim = 5; // Comprova polsador 1
else if (ADRESH > 80)
ExLim = 4; // Comprova polsador 2
else if (ADRESH > 60)
ExLim = 3; // Comprova polsador 3
else if (ADRESH > 40)
ExLim = 2; // Comprova polsador 4
else if (ADRESH > 20)
ExLim = 1; // Comprova polsador 5
else
ExLim = 0; // Comprova polsador 5
Cursor(1,15);
if(ExLim != 0){
EnviaL('+');
EnviaL('0' + ExLim);
if (BIT_CHECK(PORTC, 0) == 0) // Si el LED corresponent és apagat, engega'l
BIT_SET(PORTC, 0);
} else {
if (BIT_CHECK(PORTC, 0) == 1) // Si el LED corresponent és encès, apaga'l
BIT_CLEAR(PORTC, 0);
EnviaL(' ');
EnviaL(' ');
}
ADCON0 = 0b00001001; // Posa l'ADC a la pota dels polsadors
}

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