Relógio Digital com Alarme na PK2LAB

Olá! Este artigo demonstra a implementação de um relógio digital com alarme na placa PK2LAB utilizando o PIC18F4520 e o compilador C18. O programa demonstra como utilizar múltiplas interrupções e o modo de dupla prioridade de interrupção do PIC18.

Esta aplicação utiliza o display LCD da placa para apresentação da hora atual e a hora de alarme. O led conectado ao pino RC0 muda de estado a cada segundo e o led conectado ao pino RC1 indica se o alarme está habilitado ou não.

As teclas conectadas aos pinos RD0, RD1 e RD2 permitem incrementar a hora, minuto e segundo atuais. Pressionando-se a tecla conectada ao pino RD3 em conjunto com as teclas conectadas aos pinos RD0, RD1 ou RD2, podemos ajustar o horário do alarme.

A tecla conectada ao pino RD7 permite habilitar ou desabilitar o alarme. Ao atingir o horário selecionado, o buzzer irá soar, pressionando-se a tecla conectada ao pino RD3 podemos fazer cessar o alarme (que será disparado novamente quando o horário do alarme for atingido).

Para que o programa opere corretamente na placa, o DIP LCD e os DIPS LED_C e RD0 a RD7 devem estar na posição ENABLE.

Nós utilizamos o timer 0 para fornecer a base de tempo principal de 1 segundo para o relógio e o timer 1 para a geração de som e temporização auxiliar de 1ms (utilizada na leitura das teclas e temporização de som).

O código da função principal é bastante reduzido. Basicamente há um loop infinito onde o programa verifica se há uma requisição de atualização de display pendente (variável update=1) e se o timer de varredura do teclado atingiu o valor zero (ele é sempre recarregado com 25 dentro da função processa_teclas() ).

void main(void)
{
	MCU_init();
  	while(1)
  	{
		if (update) atualiza_display();
		if (!timer_tecla) processa_teclas();
  	}
}

O código da função de atualização do display é simples:

void atualiza_display(void)
{
	update = 0;
	LCD_pos_xy(1,0);
	LCD_write_string_rom("Hora: ");
	LCD_write_char(hora/10+'0');
	LCD_write_char(hora%10+'0');
	LCD_write_char(':');
	LCD_write_char(minuto/10+'0');
	LCD_write_char(minuto%10+'0');
	LCD_write_char(':');
	LCD_write_char(segundo/10+'0');
	LCD_write_char(segundo%10+'0');
	LCD_pos_xy(0,1);
	LCD_write_string_rom("Alarme:");
	LCD_write_char(alh/10+'0');
	LCD_write_char(alh%10+'0');
	LCD_write_char(':');
	LCD_write_char(alm/10+'0');
	LCD_write_char(alm%10+'0');
	LCD_write_char(':');
	LCD_write_char(als/10+'0');
	LCD_write_char(als%10+'0');
}

O código da função de processamento do teclado é um pouco mais complexo e merece uma descrição mais detalhada. Ela é executada aproximadamente a cada 25ms (controlada através da variável timer_tecla que é decrementada pela interrupção do CCP1 a cada 1 ms). Cada tecla é lida e se encontrada pressionada a ação correspondente é executada (incrementar hora, minuto, segundo ou habilitar/desabilitar o alarme) e um timer (timer_pressao) é incrementado.

Se a tecla permanece pressionada este timer é incrementado e ao atingir um valor superior a 20 a ação correspondente é novamente executada o o timer é recarregado com o valor 10 (no caso da tecla de hora) ou 15 (no caso das teclas de minuto e segundo). Desta forma, implementamos de forma simples um mecanismo de auto repetição quando a tecla permanece pressionada. Utilizando o limite de 20, temos a primeira auto repetição em 20 * 25ms = 500ms e as próximas em 10 * 25ms = 250ms aproximadamente.

Quando nenhuma tecla está pressionada, o timer de pressao é zerado.

void processa_teclas(void)
{
	static unsigned char timer_pressao;
	unsigned char tecla_pres;
	INTCON = 0;			// desabilita interrupções
	timer_tecla = 25;
	INTCON = bGIEH | bGIEL | bTMR0IE;	// habilita interrupções
	tecla_pres = 0;
	if (TCL_HORA)			// se a tecla HORA estiver pressionada
	{
		if (!timer_pressao || timer_pressao>20)
		{
			if (TCL_ALARME)
			{
				alh++;
				if (alh>23) alh = 0;
			} else
			{
				hora++;
				if (hora>23) hora = 0;
			}
			if (timer_pressao>20) timer_pressao = 10;
			update = 1;
		}
		tecla_pres = 1;
		timer_pressao++;
	}
	if (TCL_MINUTO)			// se a tecla MINUTO estiver pressionada
	{
		if (!timer_pressao || timer_pressao>20)
		{
			if (TCL_ALARME)
			{
				alm++;
				if (alm>59) alm = 0;
			} else
			{
				minuto++;
				if (minuto>59) minuto = 0;
			}
			if (timer_pressao>20) timer_pressao = 15;
			update = 1;
		}
		tecla_pres = 1;
		timer_pressao++;
	}
	if (TCL_SEGUNDO)		// se a tecla SEGUNDO estiver pressionada
	{
		if (!timer_pressao || timer_pressao>20)
		{
			if (TCL_ALARME)
			{
				als++;
				if (als>59) als = 0;
			} else
			{
				segundo++;
				if (segundo>59) segundo = 0;
			}
			if (timer_pressao>20) timer_pressao = 15;
			update = 1;
		}
		tecla_pres = 1;
		timer_pressao++;
	}
	if (TCL_ALON) 		// se a tecla ALON (RD7) estiver pressionada
	{
		if (!timer_pressao) alon = !alon;
		timer_pressao++;
		if (timer_pressao>20) timer_pressao = 20;
		tecla_pres = 1;
		if (alon) LED_ALON = 1; else LED_ALON = 0;
	}
	if (!tecla_pres) timer_pressao = 0;
	if (TCL_ALARME) alarme = 0;
}

O restante do código pode ser visto no arquivo fonte da aplicação. O projeto completo para o MPLAB pode ser baixado aqui.

Leave a Reply