Programació en C del PIC 16F690

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

Rellotge

Programa del grup 1

Aquest dispositiu mostra la data i l'hora. A més, es pot configurar una alarma que fa sonar una melodia a l'hora programada.

Per a la gestió del temps fan servir el temporitzador 0 però quasi tota la gestió es fa al programa principal, la funció d'interrupció només incremeta la variable que permet determinar quan ha passat un segon.

El programa és el següent:

#pragma config FOSC = INTRCIO  // Oscil·lador intern, utilitzat per al rellotge del microcontrolador
// Desactivar el watchdog timer, Power-up Timer, MCLR i protecció de codi
#pragma config WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF
// Desactivar altres funcions com la detecció de canvi de font d'oscil·lador
#pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF
#include <xc.h>  // Biblioteca per a l'ús del microcontrolador PIC
#define _XTAL_FREQ 4000000  // Definir la freqüència de rellotge a 4 MHz
// Assignació de variables per al control de l'estat del rellotge i l'alarma
#define BTN0 PORTAbits.RA3  // Definir el botó per a l'entrada de la porta A3
unsigned char h = 23, m = 59, s = 45, d = 31, mo = 12, Compta = 0;  // Variables per a la data i hora
unsigned char y = 24;  // Any 2024
char Digits[14];  // Array per a les xifres de la pantalla LCD
unsigned char alarma_h = 7, alarma_m = 0;  // Hora i minut de l'alarma
unsigned char alarma_activa = 0;  // Estat de l'alarma (activada o desactivada)
unsigned char alarma_sonando = 0;  // Indica si l'alarma està sonant o no
unsigned char modo = 0;  // Mode actual de l'aparell (hora, alarma, etc.)
// Taula de dies per cada mes
const unsigned char dies_mes[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
// Notes per a la melodia
const unsigned char notes[][4] = {  // {PR2, CCPR1L, DC1B, Duració}
  {191, 143, 0, 14}, {0, 0, 0, 6},  {170, 128, 0, 14}, {0, 0, 0, 6},
  {160, 120, 0, 14}, {0, 0, 0, 6},  {191, 143, 0, 14}, {0, 0, 0, 6},
  {143, 107, 0, 14}, {0, 0, 0, 6},  {191, 143, 0, 10}, {0, 0, 0, 10},
  {170, 128, 0, 14}, {0, 0, 0, 6},  {160, 120, 0, 14}, {0, 0, 0, 6},
  {191, 143, 0, 14}, {0, 0, 0, 6},  {143, 107, 0, 14}, {0, 0, 0, 6},
  {191, 143, 0, 10}, {0, 0, 0, 10}, {170, 128, 0, 14}, {0, 0, 0, 6},
  {160, 120, 0, 14}, {0, 0, 0, 6},  {143, 107, 0, 14}, {0, 0, 0, 6},
  {127, 95,  0, 10}, {0, 0, 0, 10}, {143, 107, 0, 14}, {0, 0, 0, 6},
  {127, 95,  0, 14}, {0, 0, 0, 6},  {191, 143, 0, 18}
};
// Variables per al control de la melodia i el PWM
unsigned char nota_activa = 0;
unsigned int nota_timer = 0;
unsigned char melod_ind = 0;
unsigned char melodia_timer = 0;
#define NUM_NOTES (sizeof(notes) / sizeof(notes[0]))
// Funció per desactivar el PWM de la melodia
void SilenciaPWM(void) {
    T2CONbits.TMR2ON = 0;
    CCP1CON = 0b00000000;
    PORTCbits.RC5 = 0;
}
// Funció per inicialitzar la nota en PWM
void IniciaNotaPWM(char ValPR2, char ValCCPR1L, char ValDC1B, unsigned char duracio) {
    SilenciaPWM();
    if (ValPR2 == 0) {
        nota_activa = 1;
        nota_timer = duracio * 50;
        return;
    }
    PR2 = ValPR2;
    CCPR1L = ValCCPR1L;
    CCP1CON = 0b00001100 | ((ValDC1B & 0x03) << 4);
    T2CONbits.TMR2ON = 1;
    nota_timer = duracio * 50;
    nota_activa = 1;
}
// Funció per gestionar el timer de la nota PWM
void GestionaNotaPWM(void) {
    if (nota_activa) {
        if (nota_timer == 0) {
            SilenciaPWM();
            nota_activa = 0;
        } else {
            nota_timer--;
        }
    }
}
// Funció per reproduir la melodia
void ReproduirMelodiaPWM(void) {
    if (!nota_activa && melodia_timer == 0) {
        IniciaNotaPWM(notes[melod_ind][0], notes[melod_ind][1], notes[melod_ind][2], notes[melod_ind][3]);
        melod_ind = (melod_ind + 1) % NUM_NOTES;
        melodia_timer = 10;
    } else if (!nota_activa) {
        melodia_timer--;
    }
}
// Funció per llegir l'estat dels polsadors
char Polsador(void) {
    char Pols = 0;
    ADCON0bits.GO = 1;
    while (ADCON0bits.GO);
    if (ADRESH < 225 && ADRESH > 200) Pols = 1;
    if (ADRESH <= 190 && ADRESH > 175) Pols = 2;
    if (ADRESH <= 158 && ADRESH > 145) Pols = 3;
    if (ADRESH <= 85 && ADRESH > 60)  Pols = 4;
    if (ADRESH <= 46 && ADRESH > 20)  Pols = 5;
    return Pols;
}
// Funció per enviar un caràcter a la pantalla LCD
void EnviaL(char Caracter) {
    while (!TXSTAbits.TRMT);
    TXREG = Caracter;
}
// Funció per esborrar la pantalla LCD
void Esborra(void) {
    EnviaL(254);
    EnviaL(1);
    __delay_ms(2);
}
// Funció per convertir un enter a una cadena de dígits
void IntToDigits(char *dest, unsigned int value, char digits) {
    for (char i = 0; i < digits; i++) {
        dest[digits - 1 - i] = (value % 10) + '0';
        value /= 10;
    }
}
// Funció per mostrar l'hora a la pantalla LCD
void MostrarHora(unsigned char hora, unsigned char minuto) {
    char Digits[5];
    IntToDigits(Digits, hora, 2);
    Digits[2] = '.';
    IntToDigits(&Digits[3], minuto, 2);
    for (char i = 0; i < 5; i++) EnviaL(Digits[i]);
}
// Funció per mostrar la data a la pantalla LCD
void MostrarFecha(unsigned char dia, unsigned char mes, unsigned char any) {
    char Digits[10];
    IntToDigits(Digits, dia, 2);
    Digits[2] = '-';
    IntToDigits(&Digits[3], mes, 2);
    Digits[5] = '-';
    Digits[6] = '2'; Digits[7] = '0';
    IntToDigits(&Digits[8], any, 2);
    for (char i = 0; i < 10; i++) EnviaL(Digits[i]);
}
// Funció per actualitzar l'hora i data, i comprovar l'alarma
void actualitzaHora(void) {
    if (Compta >= 20) {
        Compta = 0;
        if (++s == 60) {
            s = 0;
            if (++m == 60) {
                m = 0;
                if (++h == 24) {
                    h = 0;
                    d++;
                }
            }
        }
        unsigned char max_dies = dies_mes[mo - 1];
        if (mo == 2 && ((y + 2000) % 4 == 0)) max_dies = 29;
        if (d > max_dies) { d = 1; mo++; }
        if (mo > 12) { mo = 1; y++; }
        Esborra();
        // Mostrar l'estat segons el mode seleccionat
        if (modo == 0) {
            MostrarHora(h, m);
            EnviaL(' ');
            MostrarFecha(d, mo, y);
            EnviaL(' ');
            const char *estado = alarma_activa ? "ACT" : "DCT";
            for (char i = 0; i < 3; i++) EnviaL(estado[i]);
        } else if (modo == 1) {
            MostrarHora(alarma_h, alarma_m);
            EnviaL(' ');
            const char *estado = alarma_activa ? "ACT" : "DCT";
            for (char i = 0; i < 3; i++) EnviaL(estado[i]);
        } else if (modo == 2) {
            MostrarHora(h, m);
            EnviaL(' '); EnviaL('H'); EnviaL('O'); EnviaL('R'); EnviaL('A');
        } else if (modo == 3) {
            MostrarFecha(d, mo, y);
            EnviaL(' '); EnviaL('D'); EnviaL('A'); EnviaL('T'); EnviaL('A');
        }
        // Comprovar si l'alarma ha de sonar
        if (alarma_activa && h == alarma_h && m == alarma_m && s == 0 && !alarma_sonando) {
            alarma_sonando = 1;
        }
    }
}
// Funció per gestionar la interacció amb els botons
void revisaBotons(void) {
    static unsigned char old_state = 1;
    if (!BTN0 && old_state) {
        __delay_ms(20);
        if (!BTN0) {
            if (alarma_sonando) {
                alarma_sonando = 0;
            } else {
                modo = (modo + 1) % 4;
            }
            while (!BTN0);
            __delay_ms(10);
        }
    }
    old_state = BTN0;
    if (alarma_sonando) return;
    // Comprovar l'estat del botó i canviar el mode o l'alarma
    char p = Polsador();
    switch (modo) {
        case 1:
            if (p == 1) { alarma_activa = !alarma_activa; __delay_ms(300); }
            if (p == 2) { alarma_h = (alarma_h + 1) % 24; __delay_ms(300); }
            if (p == 4) { alarma_m = (alarma_m + 1) % 60; __delay_ms(300); }
            if (p == 3) { alarma_h = (alarma_h == 0) ? 23 : alarma_h - 1; __delay_ms(300); }
            if (p == 5) { alarma_m = (alarma_m == 0) ? 59 : alarma_m - 1; __delay_ms(300); }
            break;
        case 2:
            if (p == 2) { h = (h + 1) % 24; __delay_ms(300); }
            if (p == 4) { m = (m + 1) % 60; __delay_ms(300); }
            if (p == 3) { h = (h == 0) ? 23 : h - 1; __delay_ms(300); }
            if (p == 5) { m = (m == 0) ? 59 : m - 1; __delay_ms(300); }
            break;
        case 3:
            if (p == 2) { d = (d % 31) + 1; __delay_ms(300); }
            if (p == 3) { mo = (mo % 12) + 1; __delay_ms(300); }
            if (p == 4) { y = (y == 99) ? 0 : y + 1; __delay_ms(300); }
            if (p == 5) { y = (y == 0) ? 99 : y - 1; __delay_ms(300); }
            break;
    }
}
// Funció d'interrupt per comptar el temps (timer)
void __interrupt() ISR(void) {
    if (INTCONbits.T0IF) {
        INTCONbits.T0IF = 0;
        TMR0 = 61;
        Compta++;
    }
}
// Funció principal
void main(void) {
    CCP1CON = 0b00001100;  // Configurar CCP per al PWM
    TRISCbits.TRISC5 = 0;  // Configurar el pin RC5 com a sortida per al PWM
    T2CON = 0b00000111;    // Configurar el Timer 2
    SilenciaPWM();  // Inicialitzar el PWM a 0
    ANSEL = 0b00000100;  // Configurar l'entrada analògica
    ANSELH = 0;
    TRISAbits.TRISA3 = 1;  // Configurar el botó en el port A3 com a entrada
    // Configurar ADC
    ADCON0 = 0b00001001;
    ADCON1 = 0b00010000;
    // Configurar UART per la comunicació sèrie
    TXSTAbits.BRGH = 1;
    BAUDCTLbits.BRG16 = 0;
    TXSTAbits.SYNC = 0;
    RCSTAbits.SPEN = 1;
    TXSTAbits.TXEN = 1;
    SPBRG = 25;
    // Configurar Timer0 per al comptador
    OPTION_REGbits.T0CS = 0;
    OPTION_REGbits.PSA = 0;
    OPTION_REGbits.PS = 0b111;
    TMR0 = 61;
    INTCONbits.T0IE = 1;
    INTCONbits.T0IF = 0;
    INTCONbits.GIE = 1;
    // Inicialitzar els ports
    PORTA = 0;
    PORTB = 0;
    PORTC = 0;
    __delay_ms(1000);
    // Bucle principal
    while (1) {
        revisaBotons();  // Comprovar si s'ha pressionat algun botó
        GestionaNotaPWM();  // Gestionar el PWM per la melodia
        actualitzaHora();  // Actualitzar l'hora i la data

        // Si l'alarma està sonant, reproduir la melodia
        if (alarma_sonando) {
            ReproduirMelodiaPWM();
            if (!BTN0) {
                __delay_ms(20);
                if (!BTN0) {
                    alarma_sonando = 0;
                    SilenciaPWM();
                    while (!BTN0);
                    __delay_ms(10);
                }
            }
        }
    }
}

 

 

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