Este artigo mostra o projeto de um Relógio Digital simples com 4 displays do tipo 7 Segmentos para fins didáticos.
Figura 1 - Display 7 segmentos do Relógio Digital
Para a montagem são utilizados poucos componentes:
- 1 microcontrolador PIC16F628;
- 4 displays 7 segmentos;
- 2 botões para ajuste de hora e minuto;
- 8 resistores de 330 ohms para os segmentos do display;
- 2 resistores de 10 kohms para pullup dos botões;
- 4 resistores de 1 kohms para acionamento dos transistores.
O diagrama de interligação é mostrado na Figura 2.
Figura 2 - Diagrama do Circuito
A montagem pode ser feita em Matriz de Contatos. Veja a Figura 3.
Figura 3 - Circuito montado em Matriz de Contatos
O Vídeo a seguir mostra a Simulação do circuito no Proteus.
Video Relógio Digital rodando no Proteus
O programa do PIC
O programa do Relógio Digital foi escrito em Linguagem C no MPLAB IDE e compilado com o CCS. No Anexo 1 você encontra o código fonte.
A interrupção do Timer0 é utilizada como base de tempo para o relógio (clock).
Todo programa escrito em C inicia-se com a função main ( ).
Após a inicialização algumas variáveis são carregadas com seus valores iniciais.
A função config_microcontrolador( ) se encarrega de configurar os pinos do microcontrolador como entrada (valor 1) ou saída (valor 0) através dos registros TRISA e TRISB. Os comparadores analógicos internos são desabilitados, pois não são utilizados.
Em setup_timer_0 ( ) é configurado o temporizador timer0 para utilizar a fonte de pulsos internos com presscaler em 1:8 (divisão de pulsos). Esta configuração proporciona um tempo de 2048 us para cada interrupção.
No final das configurações são habilitadas as interrupções do Timer0 e global.
O programa passa então para um loop infinito, em while(true), lendo o status dos botões 1 e 2 para ajuste do minuto e da hora do relógio respectivamente.
A rotina de interrupção do Timer0 é chamada a cada 2048 us.
A cada interrupção é alternado o display que fica aceso, seguindo um processo de multiplexação. Pela cintilação do olho humano enxergamos os 4 displays acesos, mas na verdade apenas um se mantém ligado, e são alternados a cada 2048 us!
A cada interrupção a variável timer é incrementada.
A cada 29297 interrupções é completado 1 minuto:
29297 x 2048 us = 60000,256 ms ~ 1 minuto
É incrementada a variável do minuto e se necessário a variável de hora.
O horário do relógio é atualizado.
A cada 500 ms o "ponto" do relógio é invertido de status, para "piscar" a cada segundo.
Os "2 pontos" do relógio são formados invertendo-se a montagem do terceiro display de 7 segmentos em 180°.
Você agora pode implementar o código e criar um horário de alarme para adicionar a função de despertador para o seu Relógio Digital!
Obs:
Este é um circuito didático para ensinar o princípio de funcionamento de um relógio digital. Para menor erro na temporização utilizar cristal externo (XT) de 4 MHz ao invés do oscilador interno (RC).
Note que o minuto não é exato, mas sim de 60000,256 ms, o que acumula um erro de 0,000000042667 %.
Isso quer dizer que o relógio irá adiantar pouco mais de 2 minutos por ano!
(60 minutos x 24 horas x 365 dias = 525600 minutos/ano * 0,256 ms = 134,5536 s)
Para precisão no relógio deve-se utilzar um Relógio de Tempo Real (Real Time Clock - RTC), como por exemplo o DS1302 com cristal de 32,768 kHz ou um PIC com o módulo RTC interno.
Exemplos de circuitos com RTCC:
https://www.youtube.com/watch?v=zR0KW0H_dD4
https://www.youtube.com/watch?v=k_SVMvWyYak
O circuito também não pode ser desligado da alimentação, senão perde a hora, pois não tem bateria de backup.
Anexo 1 - O código fonte do Relógio Digital em Linguagem C
// ***********************************************************
// Relogio Digital
// ***********************************************************
// Arquivo: Relogio.c
// Microcontrolador: PIC16F628
// Compilador: CCS C Compiler com MPLAB IDE
// Rev 1.0 03/04/2014
// Rev 1.1 05/04/2014 - alterado pic p 628, adicionado ponto
// Desenvolvido por M.R.G.
// ***********************************************************
/* Relogio Digital com display 7 segmentos
com 2 teclas para ajuste de hora e minuto
utiliza interrupcao do Timer 0 para o clock
*/
// **************** M.R.G. **************************************
// **** Desenvolvido no Brasil *** Made in Brazil ***************
// ********* Build The World ************************************
// Pinos utilizados do microcontrolador:
// RA0 - display unidade
// RA1 - display dezena
// RA2 - display centena
// RA3 - display milhar
// RA4 - saida coletor aberto - botao1
// RA5 - MCLR - entrada digital - botao2
//
// RB0 - segmento ponto
// RB1 - segmento a
// RB2 - segmento b
// RB3 - segmento c
// RB4 - segmento d
// RB5 - segmento e
// RB6 - segmento f
// RB7 - segmento g
// ******** Inclui ficheiros *********************************
#include <16F628.H> // Define processador
// ******** Configuracao do microcontrolador ******************
#use delay(clock=4000000,RESTART_WDT)
// ******* confifura fuses ***********************************
#fuses INTRC_IO,PUT,WDT,BROWNOUT,NOMCLR,NOLVP,NOCPD
//XT oscilador externo com cristal
//INTRC_IO Clock interno, RA6 e RA7 como I/O
//PUT Ativa tempo de inicialização
//WDT Ativa Watch Dog Timer
//NOWDT desativa Wotch Dog Timer
//NOPROTECT nao protege memoria de programa
//NOBROWNOUT Não usa brownout reset
//NOMCLR Pino Master Clear usado como I/O
//NOLVP Não usa baixa voltagem para programação,
// pino B3 usado como I/O
//NOCPD Não protege EEPROM
//********** Declara constantes **************************************
#byte porta = 5 // endereco do port a
#byte portb = 6 // endereco do port b
#bit botao1 = porta.4 //botao de incremento de minuto
#bit botao2 = porta.5 //botao de incremento de hora
#bit ponto = portb.0 //ponto do display
//********** Declara variáveis globais ******************************
unsigned int digito, unidade, dezena, centena, milhar;
unsigned long int tempo;
// **************** Declara protótipos ******************************
void le_botao1 (void);
void le_botao2 (void);
// *************** Rotina de interrupçcao do Timer0 ***************
#int_timer0
void timer0interrupt() // 2048 us por interrupcao
{
unsigned int portAPino, portBValor, portBPino;
digito++;
if (digito == 5)
digito = 1;
// identifica o digito (display) que deve ser aceso
switch (digito){ //verifica qual digito a acender
case 1:{ //codigo para acender o display 1
portAPino = 0b00000001;
portBValor = unidade;
break;}
case 2:{ //codigo para acender o display 2
portAPino = 0b00000010;
portBValor = dezena;
break;}
case 3:{ //codigo para acender o display 3
portAPino = 0b00000100;
portBValor = centena;
break;}
case 4:{ //codigo para acender o display 4
portAPino = 0b00001000;
portBValor = milhar;
break;}
default:{
break;}
}
// carrega valor do numero a acender no diaplay
switch (portBValor){ //verifica qual valor do numero
case 0:{ //codigo para acender o digito 0
portBPino = 0b01111110;
break;}
case 1:{ //codigo para acender o digito 1
portBPino = 0b00001100;
break;}
case 2:{ //codigo para acender o digito 2
portBPino = 0b10110110;
break;}
case 3:{ //codigo para acender o digito 3
portBPino = 0b10011110;
break;}
case 4:{ //codigo para acender o digito 4
portBPino = 0b11001100;
break;}
case 5: { //codigo para acender o digito 5
portBPino = 0b11011010;
break;}
case 6: { //codigo para acender o digito 6
portBPino = 0b11111010;
break;}
case 7: { //codigo para acender o digito 7
portBPino = 0b00001110;
break;}
case 8: { //codigo para acender o digito 8
portBPino = 0b11111110;
break;}
case 9: { //codigo para acender o digito 9
portBPino = 0b11011110;
break;}
default:{
break;}
}
porta = 0; //apaga todos os digitos
portB = portBPino; //acende segmentos
//carrega valor do numero no digito
// pisca os 2 pontos do relogio
if( (bit_test(tempo,8)) && (bit_test(tempo,7)) && ( (digito == 2)||(digito == 3)) )
ponto = 1;
else
ponto = 0;
porta = portAPino; //acende o digito com multiplexacao
// ***** ajusta a hora e minuto *****
tempo++;
if (tempo >= 29297) //29297 * 2048 us = 60000,256 us = 1 minuto
{ //488 = 1 segundo
unidade++;
tempo = 0;
}
// verifica incremento de minuto e hora
if (unidade >= 10)
{
dezena++;
unidade = 0;
}
if (dezena >= 6)
{
centena++;
dezena = 0;
}
if ((centena == 4) && (milhar == 2))
{
milhar = 0;
centena = 0;
}
if (centena >= 10)
{
milhar++;
centena = 0;
}
// fim da interrupcao
clear_interrupt(int_rtcc); // clears the timer0 interrupt flag
}
//***********************************************************************
// Configura o microcontrolador
//***********************************************************************
void config_microcontrolador(void){
set_tris_A(0b11110000); //configura pinos de I/O 1 = entrada 0 = saida
set_tris_B(0b00000000); //configura pinos de I/O 1 = entrada 0 = saida
setup_comparator(nc_nc_nc_nc); //desabilita comparadores internos
setup_vref(FALSE);
setup_wdt(WDT_2304MS); //habilita watch dog timer
restart_WDT(); //zera WDT
// OPTION_REG = B'00010010' ; T0 interno, dividido por 8 (2 ms/interr)
//timer 0 habilitado
setup_timer_0 (RTCC_DIV_8|RTCC_INTERNAL); //presscaler do Timer0 1:8
//2048 us para interrupcao
set_timer0(0); //zera contador do Timer 0
clear_interrupt(int_rtcc); // clears the timer0 interrupt flag
enable_interrupts(int_rtcc); // enables the timer0 interrupt faz T0IE = 1
enable_interrupts(global);
return;
}
// ************************************************************************
// Função principal - inicio do programa
// *************************************************************************
void main() {
unsigned long int i;
// --- inicializa variáveis ---------------------
portb = 0;
digito = 0;
tempo = 0;
unidade = 0;
dezena = 0;
centena = 0;
milhar = 0;
config_microcontrolador(); //configura microcontrolador
restart_WDT();
while(TRUE) //loop infinito
{
if (!botao1) //botao 1 pressionado?
le_botao1 ();
if (!botao2) //botao 2 pressionado?
le_botao2 ();
restart_WDT(); //zera WDT
}
}
//******************************************************************************
// Le botao 1 - ajuste de minuto
//******************************************************************************
void le_botao1 ()
{ // Debouncing --> tempo para estabilizar
int t; // a tecla e evitar repique
for (t=0;t<50;t++) // debouncing verifica se tecla pressionada durante tempo t
{
delay_ms(1);
if (botao1)
{
goto sair; //tecla liberada antes do tempo
}
}
delay_cycles( 2 ); // NOP - nenhuma instrução
while (!botao1) //aguarda liberar a tecla
{
delay_cycles( 2 ); // NOP - nenhuma instrução
restart_WDT(); //zera WDT
}
disable_interrupts (global); // desabilita interrupcao global GIE
unidade++; //incrementa minuto
if (unidade >= 10)
{
dezena++;
unidade = 0;
}
if (dezena >= 6)
{
dezena = 0;
}
sair:
enable_interrupts(global);
restart_WDT(); //zera WDT
return;
}
//******************************************************************************
// Le botao 2 - ajuste de hora
//******************************************************************************
void le_botao2 ()
{ // Debouncing --> tempo para estabilizar
int t; // a tecla e evitar repique
for (t=0;t<50;t++) // debouncing verifica se tecla pressionada durante tempo t
{
delay_ms(1);
if (botao2)
{
goto sair2; //tecla liberada antes do tempo
}
}
delay_cycles( 2 ); // NOP - nenhuma instrução
while (!botao2) //aguarda liberar a tecla
{
delay_cycles( 2 ); // NOP - nenhuma instrução
restart_WDT(); //zera WDT
}
disable_interrupts (global); // desabilita interrupcao global GIE
centena++; //incrementa hora
if ((centena == 4) && (milhar == 2))
{
milhar = 0;
centena = 0;
}
else if (centena == 10)
{
milhar ++;
centena = 0;
}
sair2:
enable_interrupts(global);
restart_WDT(); //zera WDT
return;
}
//******************************** M.R.G.***********************************