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

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