Mostrando postagens com marcador pic16f628. Mostrar todas as postagens
Mostrando postagens com marcador pic16f628. Mostrar todas as postagens

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