Mostrando postagens com marcador relogio digital. Mostrar todas as postagens
Mostrando postagens com marcador relogio digital. Mostrar todas as postagens

quinta-feira, 4 de junho de 2015

Relógio Digital com o 8051

Que tal montar seu próprio Relógio Digital com um 8051?


Este artigo mostra o projeto de um Relógio Digital simples com 4 displays do tipo 7 Segmentos para fins didáticos.



Para a montagem é utilizada a lista de componentes:

- 1 microcontrolador da família 8051 (Atmel AT89S52);
- 4 displays 7 segmentos catodo comum (-);
- 4 transistores NPN BC337 para drive dos displays.
- 8 transistores PNP BC327 para drive dos segmentos.
- 2 botões para ajustes de hora e minuto;
- 1 cristal de 11,0592 MHz
- 2 capacitores de 33 pF para o cristal.
- 8 resistores de 330 ohms para os segmentos do display;
- 2 resistores de 10 kohms para pullup dos botões;
- 12 resistores de 10 kohms para acionamento dos transistores.

O circuito é alimentado com 5 Vcc.

O diagrama de interligação é mostrado na Figura 1.
 
Figura 1 - Diagrama do Relógio Digital.

A montagem pode ser feita em Matriz de Contatos. Veja a Figura 2.
 
Figura 2 - Montagem do Circuito em Matriz de Contatos.


O Vídeo a seguir mostra o Relógio Digital funcionando e a Simulação do circuito no Proteus.

 Vídeo 1 - Nosso Relógio Digital com o 8051 funcionando.


O programa do 8051

O programa do Relógio Digital foi escrito em Linguagem Assembly e pode ser compilado com os Compiladores ASM51 ou Keil. No Anexo 1 você encontra o código fonte.

A interrupção do Timer1 é utilizada como base de tempo para o relógio (clock).

O programa inicia no endereço 100H.

Após a inicialização algumas variáveis são carregadas com seus valores iniciais. 


O temporizador Timer1 é configurado para operar no modo 2: 8-bit com recarga automática. 

Os registros TL1 e TH1, carga e recarga do Timer1, são setados com valor (255 - 225).

No final das configurações são habilitadas as interrupções do Timer1 e global.

O Timer1 é inicializado.

O programa passa a rodar em um loop infinito verificando se os botões de ajustes de hora e minuto foram pressionados e atualizando o display.

A rotina de interrupção do Timer1 é chamada a cada 244,140625 us.
 
Os cálculos para definir a carga do Timer1 com valor (255 - 225) estão disponíveis no Quadro 1:


; CLOCK DO RELOGIO BASEADO NA INTERRUPCAO DO TIMER 1 
; OPERANDO NO MODO 1 - 8 BITS COM RECARGA AUTOMATICA
;
; CALCULOS
; FREQUENCIA DO CRISTAL f = 11.059.200 MHz
; PERIODO DO CRISTAL T = 1/f  =  9,04224537037037...e-8     s
; PERIODO DE UMA INSTRUCAO     = T * 12 =     1,085069444...e-6  s
; NUMERO DE INSTRUCOES EM 1 SEGUNDO = 921.600 IPS
; CARREGAMENTO DO TIMER 1 = 921.000 / 4096 = 225
; 4096 DECIMAL = 1000 HEXADECIMAL = 1 0000 0000 0000 BINARIO

Quadro 1 - Cálculos para definir o tempo da interrupção do Timer1.

As variáveis CONTADOR_L e CONTADOR_H armazenam o número de interrupções.

A cada 4096 interrupções (1000H em hexadecimal) é completado 1 segundo. A variável SEGUNDO é incrementada em 1 unidade e quando necessário as demais variáveis são incrementadas: 

- UNIDADE, para a unidade dos minutos do relógio;
- DEZENA, para dezena dos minutos do relógio;
- CENTENA, para a unidade das horas do relógio;
- MILHAR, para dezena das horas do relógio.

Este relógio possui o formato de 24 horas.

O acionamento do display é feito por multiplexação. Cada dígito fica ligado por um tempo de 1 ms. Pela cintilação do olho humano enxergamos os 4 displays acesos, mas na verdade apenas um se mantém ligado!

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°.

Após compilar o seu projeto com o ASM51 ou Keil você deve programar o microcontrolador 8051 com o arquivo .hex. O vídeo 8051 - O primeiro projeto - Pisca LED - Compilação e Gravação do AT89S52  mostra um exemplo de como programar o AT89S52.



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 melhor 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 microcontrolador 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.


Referência:

NICOLOSI, D. E. C.; Laboratório de Microcontroladores: Treino de instruções, hardware e software. São Paulo: Érica, 2002.



Anexo 1 - Código fonte do Relógio Digital com o 8051 em Assembly 

; ############ RELOGIO DIGITAL COM 4 DISPLAYS DE 7 SEGMENTOS ########################

; RELOGIO DIGITAL COM HORA MINUTO E 2 PONTOS
; AJUSTE DE HORA E MINUTO ATRAVES DE 2 TECLAS

; MICROCONTROLADOR 8051 - AT89S52
; COMPILADOR ASM51 ou Keil
; JUNHO/2013  M.R.G.

; SEGMENTOS DO DISPLAY NO PORT 3
; DISPLAY CATODO COMUM (-) 
; SEGMENTO ATIVO COM LOGICA NIVEL BAIXO (TRANSISTOR PNP PARA CADA SEGMENTO)

; DISPLAY UNIDADE P2.0
; DISPLAY DEZENA  P2.1
; DISPLAY CENTENA P2.2
; DISLAY MILHAR   P2.3

; BOTAO 1 P2.4
; BOTAO 2 P2.5

; CLOCK DO RELOGIO BASEADO NA INTERRUPCAO DO TIMER 1 
; OPERANDO NO MODO 1 - 8 BITS COM RECARGA AUTOMATICA
; CALCULOS
; FREQUENCIA DO CRISTAL f = 11.059.200 MHz
; PERIODO DO CRISTAL T = 1/f  =  9,04224537037037...e-8     s
; PERIODO DE UMA INSTRUCAO     = T * 12 =     1,085069444...e-6  s
; NUMERO DE INSTRUCOES EM 1 SEGUNDO = 921.600 IPS
; CARREGAMENTO DO TIMER 1 = 921.000 / 4096 = 225
; 4096 DECIMAL = 1000 HEXADECIMAL = 1 0000 0000 0000 BINARIO


$MOD51

; *************** DEFINE CONSTANTES   ********************************** 
        TEMPO10MS      EQU      9210
        TEMPO1MS       EQU      921
        DISPU          EQU      P2.0
        DISPD          EQU      P2.1
        DISPC          EQU      P2.2
        DISPM          EQU      P2.3
        BOTAO1         EQU      P2.4
        BOTAO2         EQU      P2.5

; ******************** DECLARACAO DE VARIAVEIS DA RAM ******************

        UNIDADE        EQU 60H       ;NUMERO
        DEZENA         EQU 61H       ;NUMERO
        CENTENA        EQU 62H       ;NUMERO
        MILHAR         EQU 63H       ;NUMERO
        UNIDADE7SEG    EQU 64H       ;CARACTERE
        DEZENA7SEG     EQU 65H       ;CARACTERE
        CENTENA7SEG    EQU 66H       ;CARACTERE
        MILHAR7SEG     EQU 67H       ;CARACTERE
        CONTADOR_L     EQU 68H       ;LSB CONTADOR INTERRUPCOES T1
        CONTADOR_H     EQU 69H       ;MSB CONTADOR INTERRUPCOES T1
        SEGUNDO        EQU 6AH       ;ARMAZENA OS SEGUNDOS
        PONTO          EQU 00H       ;BIT UTILIZADO PARA PISCAR 2 PONTOS

;*************** VETOR DE RESET ****************************************

        ORG    000H

        NOP
        NOP
        LJMP INICIO

;************ VETOR DE INTERRUPCAO DO TIMER    1***************************

        ORG  01BH

        LJMP INT_TIMER1

;************************** MAIN ***************************************
        ORG    100H

INICIO:    
        MOV SP, #30H    ;ALTERA ENDERECO DA BASE DA PILHA

;DESLIGA DISPLAYS
        MOV P3, #0FFH    ;DESATIVA TODOS OS SEGMENTOS
        CLR DISPU         ;DESATIVA TODOS OS DISPLAY
        CLR DISPD
        CLR DISPC
        CLR DISPM


;INICIALIZA VARIAVEIS DA RAM
        MOV        UNIDADE, #0        
        MOV        DEZENA, #0        
        MOV        CENTENA, #0        
        MOV        MILHAR, #0        
        MOV        CONTADOR_L, #0    
        MOV        CONTADOR_H, #0    
        MOV        SEGUNDO, #0
        CLR        PONTO        


;CONFIGURA TIMERS

        MOV    TMOD,#00100001B   ;TIMER 0 MODO 1 - 16 BITS
                                 ;TIMER 1 MODO 2 - 8 BITS COM RECARGA AUTOMATICA
        MOV TL1, #(255 - 225)    ;CARGA DO TIMER 1
        MOV TH1, #(255 - 225)    ;RECARGA DO TIMER 1


        LCALL ESCREVE7SEG        ;TESTE DISPLAYS


        SETB EA                 ;HABILITA FLAG DE INTERRUPCAO GERAL
        SETB ET1                ;HABILITA FLAG DE INTERRUPCAO DO TIMER 1
        
        SETB TR1                ;INICIA TEMPORIZADOR TIMER1



LOOP:
        JB BOTAO1, TESTA_BT2        ;SE TECLA NAO PRESSIONADA(1) DESVIA
        LCALL ACERTA_MINUTO
TESTA_BT2:
        JB BOTAO2, ACIONA_DISPLAY   ;SE TECLA NAO PRESSIONADA(1) DESVIA
        LCALL ACERTA_HORA
ACIONA_DISPLAY:
        LCALL ATUALIZA_DISPLAY

        LJMP LOOP





;AJUSTE MANUAL DO MINUTO PELO BOTAO 1
ACERTA_MINUTO:
        INC UNIDADE
        MOV A, UNIDADE
        CJNE A, #10D, INCREMENTO_MIN_FIM
ACERTA_DEZ_MINUTO:
        MOV UNIDADE, #0
        INC DEZENA
        MOV A, DEZENA
        CJNE A, #06D, INCREMENTO_MIN_FIM
        MOV DEZENA, #0
INCREMENTO_MIN_FIM:
        RET


;AJUSTE MANUAL DA HORA PELO BOTAO 2
ACERTA_HORA:
        INC CENTENA
        MOV A, CENTENA
        CJNE A, #04D, ACERTA_DEZ_HORA        ;VERIFICA SE COMPLETOU 24 HORAS
        MOV A, MILHAR
        CJNE A, #02D, INCREMENTO_HORA_FIM
        MOV CENTENA, #0                      ;COMPLETOU 24 HORAS ZERA O RELOGIO
        MOV    MILHAR, #0
        AJMP INCREMENTO_HORA_FIM
ACERTA_DEZ_HORA:
        CJNE A, #10D, INCREMENTO_HORA_FIM
        MOV CENTENA, #0
        INC MILHAR
INCREMENTO_HORA_FIM:
        RET





;SUBROTINA PARA ESCRITA NOS DISPLAY
ESCREVE7SEG:
        MOV P3, #00000000B    ;ACENDE TODOS OS SEGMENTOS
        LCALL MUX7SEG

;SUBROTINA MULTIPLEXA NUMERO NO DISPLAY    - VISUALIZA 1 DISPLAY ACESO POR VEZ
MUX7SEG:
          CLR DISPU           ;DESATIVA TODOS OS DISPLAY
        CLR DISPD
        CLR DISPC
        CLR DISPM

        SETB DISPU            ;LIGA DISPLAY DA UNIDADE
        LCALL DELAY500MS      ;DELAY
        CLR DISPU             ;DESLIGA DISPLAY DA UNIDADE
        NOP

        SETB DISPD            ;LIGA DISPLAY DA DEZENA
        LCALL DELAY500MS      ;DELAY
        CLR DISPD             ;DESLIGA DISPLAY DA DEZENA
        NOP

        SETB DISPC            ;LIGA DISPLAY DA CENTENA
        LCALL DELAY500MS      ;DELAY
        CLR DISPC             ;DESLIGA DISPLAY DA CENTENA
        NOP

        SETB DISPM            ;LIGA DISPLAY DO MILHAR
        LCALL DELAY500MS      ;DELAY
        CLR DISPM             ;DESLIGA DISPLAY DO MILHAR
        NOP

          RET


; ############## ATUALIZA DISPLAY 7 SEGMENTOS ##########################
ATUALIZA_DISPLAY:

    
        LCALL CONVERTE            ;CONVERTE NUMERO NO CARACTER CORRESPONDENTE 
                                  ;PARA O DISPLAY 7 SEGMENTOS

           MOV R3, #50D           ;4 X 50 X 1ms  = 200 ms

;FAZ A MULTIPLEXACAO DOS 4 DISPLAYS DE 7 SEGMENTOS
LOOP_DISPLAY_CONT2:
        MOV P3, UNIDADE7SEG    ;MOVE CARACTER DA UNIDADE PARA DISPLAY
        NOP
        NOP
        SETB DISPU            ;LIGA DISPLAY DA UNIDADE
        LCALL DELAY1MS        ;DELAY
        CLR DISPU             ;DESLIGA DISPLAY DA UNIDADE
        NOP
        NOP

        MOV A, DEZENA7SEG    ;MOVE CARACTER DA DEZENA PARA ACC
        MOV C, PONTO         ;MOVE VALOR DO PONTO DECIMAL
        MOV ACC.7, C
        MOV P3, A            ;MOVE CARACTER DA DEZENA PARA DISPLAY COM PONTO
        NOP
        NOP
        SETB DISPD            ;LIGA DISPLAY DA DEZENA
        LCALL DELAY1MS        ;DELAY
        CLR DISPD             ;DESLIGA DISPLAY DA DEZENA
        NOP
        NOP

        MOV A, CENTENA7SEG    ;MOVE CARACTER DA CENTENA PARA ACC
        MOV C, PONTO          ;MOVE VALOR DO PONTO DECIMAL
        MOV ACC.7, C
        MOV P3, A             ;MOVE CARACTER DA CENTENA PARA DISPLAY COM PONTO
        NOP 
        NOP
        SETB DISPC            ;LIGA DISPLAY DA CENTENA
        LCALL DELAY1MS        ;DELAY
        CLR DISPC             ;DESLIGA DISPLAY DA CENTENA
        NOP
        NOP

        MOV P3, MILHAR7SEG    ;MOVE CARACTER DO MILHAR PARA DISPLAY
        NOP
        NOP
        SETB DISPM            ;LIGA DISPLAY DO MILHAR
        LCALL DELAY1MS        ;DELAY
        CLR DISPM             ;DESLIGA DISPLAY DO MILHAR
        NOP
        NOP

        DJNZ R3, LOOP_DISPLAY_CONT2    ;DECREMENTA E VERIFICA SE TERMINOU OS 50 LOOPS

        RET


;  ---------------------------------------------------------

;CONVERTE NUMERO NO CARACTER CORRESPONDENTE 
CONVERTE:
        MOV A, UNIDADE
        LCALL COMPARA
        MOV UNIDADE7SEG, A

        MOV A, DEZENA
        LCALL COMPARA
        MOV DEZENA7SEG, A

        MOV A, CENTENA
        LCALL COMPARA
        MOV CENTENA7SEG, A
        
        MOV A, MILHAR
        LCALL COMPARA
        MOV MILHAR7SEG, A

        RET



;COMPARA E CARREGA ACC COM VALOR DO CARACTER 7 SEGMENTO
;CORRESPONDENTE AO ALGARISMO DECIMAL 
COMPARA:
       CJNE A, #0D, COMPARA1
       MOV A, #11000000B    ;CARACTER 0
       RET
COMPARA1:
       CJNE A, #1D, COMPARA2
       MOV A, #11111001B    ;CARACTER 1
       RET
COMPARA2:
       CJNE A, #2D, COMPARA3
       MOV A, #10100100B    ;CARACTER 2
       RET
COMPARA3:
       CJNE A, #3D, COMPARA4
       MOV A, #10110000B    ;CARACTER 3
       RET
COMPARA4:
       CJNE A, #4D, COMPARA5
       MOV A, #10011001B    ;CARACTER 4
       RET
COMPARA5:
       CJNE A, #5D, COMPARA6
       MOV A, #10010010B    ;CARACTER 5
       RET
COMPARA6:
       CJNE A, #6D, COMPARA7
       MOV A, #10000010B    ;CARACTER 6
       RET
COMPARA7:
       CJNE A, #7D, COMPARA8
       MOV A, #11111000B    ;CARACTER 7
       RET
COMPARA8:
       CJNE A, #8D, COMPARA9
       MOV A, #10000000B    ;CARACTER 8
       RET
COMPARA9:
       CJNE A, #9D, COMPARA_ERRO
       MOV A, #10010000B    ;CARACTER 9
       RET
COMPARA_ERRO:                ;ERRO - NAO E ALGARISMO DECIMAL 0 A 9
       MOV A, #0000000B      ;ACENDE TODOS SEGMENTOS
       RET





;SUBROTINA DE DELAY
DELAY500MS:
        MOV R0, #50D                        ;50 X 10 MS = 500 MS
DELAY10MS:                                  
        MOV TL0, #LOW (65535-TEMPO10MS)     ;CARREGA VALOR PARA TIMER 0
        MOV TH0, #HIGH(65535-TEMPO10MS)
        SETB TR0                           ;LIGA TIMER 0
        JNB TF0, $                         ;AGUARDA FIM DA CONTAGEM
        CLR TR0                            ;LIMPA FLAG
        CLR TF0
        DJNZ R0, DELAY10MS                 ;DECREMENTA E VERIFICA SE TERMINOU OS 50 LOOPS
        RET



;DELAY UNICO 1 MS
DELAY1MS:                                  
        MOV TL0, #LOW (65535-TEMPO1MS)   ;CARREGA VALOR PARA TIMER 0
        MOV TH0, #HIGH(65535-TEMPO1MS)
        SETB TR0                         ;LIGA TIMER 0
        JNB TF0, $                       ;AGUARDA FIM DA CONTAGEM
        CLR TR0                          ;LIMPA FLAG
        CLR TF0
        RET


DELAY20CICLO:
        MOV R4, #20
        DJNZ R4, $                        ;DELAY 
        RET


; ############# SUBROTINA DE TRATAMENTO DE INTERRUPCAO DO TIMER 1 ###########
INT_TIMER1:
        PUSH PSW                 ;ARMAZENA VALORES DOS REGISTROS PRINCIPAIS
        PUSH ACC

        MOV A, CONTADOR_L        ;MOVE BYTE LSB DO CONTADOR PARA ACC
        ADD A, #1D               ;INCREMENTA UMA UNIDADE
        JNC    COMPARA_CONT_L    ;DESVIA SE CARRY = 0 - VERIFICA OVERFLOW
        CLR A                    ;ZERA BYTE LSB SE MAIOR QUE 255
        INC CONTADOR_H           ;INCREMENTA CONTADOR H

; COMPARA SE COMPLETOU MEIO SEGUNDO = 0800H OU 1 SEGUNDO = 1000H
COMPARA_CONT_L:
        MOV CONTADOR_L, A        ;RETORNA VALOR DE ACC PARA CONTADOR

        CJNE A, #00D , INT_FIM   ; SE NAO COMPLETOU TEMPO SAI DA INTERRUPCAO

COMPARA_CONT_H:        
        MOV A, CONTADOR_H         ; MOVE BYTE MSB DO CONTADOR PARA ACC
        CJNE A, #08H , COMPARA_1SEG  ; SE NAO COMPLETOU MEIO SEGUNDO DESVIA
        CPL    PONTO                ; COMPLETOU MEIO SEGUNDO INVERTE 2 PONTOS
COMPARA_1SEG:
         CJNE A, #10H , INT_FIM  ; SE NAO COMPLETOU 1 SEGUNDO SAI DA INTERRUPCAO

        MOV CONTADOR_H, #0       ;LIMPA CONTADOR
INCREMENTA_1SEG:
        INC SEGUNDO              ;INCREMENTA SEGUNDO
        CPL PONTO                ; COMPLETOU MEIO SEGUNDO INVERTE 2 PONTOS
        MOV A, SEGUNDO
        CJNE A, #60D, INCREMENTO_FIM  ;VERIFICA SE COMPLETOU 1 MINUTO

INC_UNI_MINUTO:
        MOV SEGUNDO, #0
        INC UNIDADE
        MOV A, UNIDADE
        CJNE A, #10D, INCREMENTO_FIM
INC_DEZ_MINUTO:
        MOV UNIDADE, #0
        INC DEZENA
        MOV A, DEZENA
        CJNE A, #06D, INCREMENTO_FIM
INC_UNI_HORA:
        MOV DEZENA, #0
        INC CENTENA
        MOV A, CENTENA
        CJNE A, #04D, INC_DEZ_HORA            ;VERIFICA SE COMPLETOU 24 HORAS
        MOV A, MILHAR
        CJNE A, #02D, INCREMENTO_FIM
        MOV CENTENA, #0                       ;COMPLETOU 24 HORAS ZERA O RELOGIO
        MOV    MILHAR, #0
        AJMP INCREMENTO_FIM
INC_DEZ_HORA:
        CJNE A, #10D, INCREMENTO_FIM
        MOV CENTENA, #0
        INC MILHAR



INCREMENTO_FIM:        


INT_FIM:
        CLR TF1            ;LIMPA FLAG DE INTERRUPCAO DO TIMER 1

        POP ACC            ;RESTAURA VALORES DOS REGISTROS PRINCIPAIS
        POP PSW

        RETI               ;RETORNA DA INTERRUPCAO
        
        END    

segunda-feira, 2 de fevereiro de 2015

Relógio Digital com PIC16F628 em Linguagem C

Que tal montar seu próprio Relógio Digital?


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.***********************************