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.