CONTADOR DIGITAL UP/DOWN – COM PIC 16F628A (REF308)

Um contador up/down que salva contagem na EEprom em caso de falta de energia elétrica na rede…

Os contadores digitais são dispositivos que mostram uma contagem em um visor de LCD ou em displays de 7 seguimentos. O valor desta contagem pode ser incrementado ao apertar o botão “Up” ou decrementado se apertar o botão “Down”. Os contadores podem ser usados em muitas aplicações práticas, como placares esportivos, organizadores de fila, hodômetros, catracas eletrônicas, etc.
Este artigo tratará de um contador up/down que salva a contagem na EEprom interna do PIC. Veja o esquema abaixo:

Para controlar os processos de leitura de interruptores e multiplexação dos displays de 7 seguimentos foi usado o conhecido PIC 16F628A.
O portB aciona os seguimentos de cada display. Apenas o pino 13 foi usado como saída para um relé.
Este relé liga quando a contagem for diferente de zero e desliga em zero. Dependendo da aplicação talvez você prefira não usar esta saída.
O portA aciona os transistores drives dos displays ligados nos cátodos (ou ânodo, conforme a versão). Também serve de entrada para os interruptores de pressão momentânea para incrementar (Up) e decrementar (Down), além do interruptor para zerar a contagem e também a memória. Note que o pino 3 foi usado como um sensor de falta de energia. Em funcionamento este pino está em nível ‘1’ por meio do divisor de tensão e o diodo Zener de 5 Volts ligado na tensão de 12 Volts na fonte. Quando ocorre uma queda de tensão na rede elétrica, a tensão acumulada pelo capacitor de entrada da fonte começa a cair e quando chega em aproximadamente 8 Volts produzirá nível lógico ‘0’ no pino 3. Isto faz que a rotina salve a contagem na EEprom interna. Ao voltar a energia, na inicialização do programa, os registradores da contagem serão carregados com os valores salvos.
Por que foi usado este método de salvamento? Muitos preferem simplesmente ir salvando na EEprom a cada aperto em um dos botões. Isto significa que a memória EEprom será constantemente utilizada. Em certa aplicações, talvez tenha que acumular contagens altas e várias vezes por dia. Nesta situação, a EEprom poderá ter sua vida útil reduzida, com a perda do microcontrolador. O datasheet do PIC 16F628A informa logo na página inicial, o tempo de vida da EEprom: 40 anos para a retenção dos dados e 1 milhão de ciclos de escrita. Se queremos que ela dure o tempo de vida máximo então temos de descobrir quantas vezes podemos gravar uma mesma posição de memória por dia.

1000000 / (40 * 365 ) = 68 vezes.

No caso proposto, o salvamento ocorrerá somente ao desligar o equipamento, ou quando ocorre a falta de energia elétrica na rede, aumentando em muito a vida útil da EEprom interna.
Logicamente em montagens de pouco uso não haveria necessidade de ter esta preocupação com a vida da EEprom. Mas é sempre bom lembrar que tudo tem um tempo de vida, inclusive as memória internas do PIC e tomar cuidado em como as usamos.
Obs. Esta montagem é experimental, sendo de caráter didático, montada apenas em placa experimental (do tipo “Breadboard”), sujeita a “bugs” ainda não detectados. Está sendo fornecido os arquivos para que cada hobista possa alterar o programa segundo suas necessidades.

Segue abaixo uma pasta zipada com os arquivos da montagem. Ao descompactar, você encontrará 4 pastas, com versão usando 4 dígitos, 3 dígitos, 2 dígitos e 1 dígito. Encontrará esquemas e arquivos .hex para ânodo ou cátodo comum em cada uma das pastas.

CONTADOR UP_DOWN

Em 21/01/2018 foi postado esta nova versão, com ‘debounce’ de interruptores melhorado:

CONTADOR_U_D_E_RELE_V2_1

Em 13/06/2020 foi acrescentado esta versão alternativa, para 4 dígitos, sem botão ‘Down’, mas um botão ‘Mem’ em seu lugar. Contagem apenas crescente, com memorização do valor ao apertar botão ‘Mem’. Se o botão ‘Mem’ estiver apertado durante o pós reset, então os dígitos mostrarão o valor salvo:

contador_up_alterado

Manuais:
Datasheet do PIC16F628A

Curiosidades:
Por que parar de fumar?
O que devo saber sobre esteróides?
Conceito equilibrado sobre animais de estimação
Desatualizada ou à frente do seu tempo?
Como a vida se originou?

Outros assuntos:
O caminho para a felicidade
Como cuidar de um parente com uma doença terminal
Como controlar os gastos
Evolução ou Criação parte 1
Evolução ou Criação parte 2
Evolução ou Criação parte 3

Vídeos:
As maravilhas da criação revelam a glória de Deus
Em frente dos meus olhos
Porque Deus criou a Terra?
Aprenda a perdoar

Até o próximo artigo!

40 comments on “CONTADOR DIGITAL UP/DOWN – COM PIC 16F628A (REF308)

  1. Olá, Raimundo de novo, fiz o up down 3 digitos versao 2 ta funcionando muito bem !!, quando fiz o de 1 digito com versao 2 nao deu certo, o display fica no zero piscando, daí usei o hex da versao 1 e funcionou, depois ce da uma olhada ai, interessei pela ideia do rele ligar com 999, será que deu certo, nao sei mexer no programa, se tiver o hex ai e puder manda pra nois, grato!!!

  2. Ola estou entrando nesse mundo muito doido que e a programação só que tudo por pesquisa no Pai google, queria saber como faço para que quando chegue nos ”999” acione o rele na caso como esta e baixei e testei e funcionou 100% o rele so acende quando esta em ”0”. tentei fazer algumas mudança mais nada meu conhecimento e quase nada ainda espero a ajuda de vcs valeu ha naõ sei se muda mais queria pro contador de 3 digito.

    e parabéns ao dono do blog muito bom esta ajudando eu e muitas pessoas. espero um dia chegar nesse nível.

    1. Olá Julio Cesar!

      Faça alterações conforme abaixo e depois recompile usando o CCS C compiler para obter o novo arquivo Hex:

      //******************************************************************************
      //
      // CONTADOR UP/DOWN/EEP C/RELE
      //
      // UTILIZA PIC 16F628A E 3 DISPLAY DE 7 SEGUIMENTOS
      //
      //
      // AUTOR : CLAUDIO LARIOS
      //
      // data: 21/01/2018
      //
      // Obs: Rele liga ao chegar no 999
      //
      // Uso didático
      //
      // Blog PICSOURCE (LARIOS.TECNOLOGIA.WS)
      //
      //******************************************************************************

      #DEFINE CATODO_COMUM //descomente p/ catodo comum e comente p/ anodo comum
      //==============================================================================
      #include <16F628a.h>
      #fuses INTRC_IO,NOWDT,NOLVP,NOMCLR
      #use delay(clock=4000000) //tempo por instrução=1us
      #rom 0x2100 = {0,0,0}
      //bytes
      #byte porta = 0x05
      #byte portb = 0x06
      #byte trisa = 0x85
      #byte trisb = 0x86
      #bit gie = 0x0b.7
      #bit peie = 0x0b.6
      #bit t0ie = 0x0b.5
      //bits
      #bit zerar = 0x05.5 //interruptor ‘zerar’

      #bit sw_up = 0x05.6 //interruptor ‘up’
      #bit sw_down = 0x05.7 //interruptor ‘down’
      #bit power = 0x05.4 //sensor de falta de energia
      #bit rele = 0x06.7 //saída do rele

      //variavéis globais
      int dig2,dig1,dig0,mux,a;

      // Tabela para retorno dos seguimentos correspondentes aos números 0-9
      byte const tabela [] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

      #define VALOR_DBC 230 //valor para debounce das chaves

      //==============================================================================
      // ROTINA DE INTERRUPÇÃO DO TIMER 0
      //==============================================================================
      #INT_TIMER0
      //multiplexa display de 7 seg
      void display_refress() {
      if(++mux>2){ mux=0;}
      porta=0; //apaga seguimentos para mudança
      switch (mux) {
      case 0:
      portb&=0x80;
      portb=portb|(0X7f & tabela[dig0]);
      #IFDEF CATODO_COMUM
      portb^=0x7f;
      #ENDIF
      porta=1;
      break;
      case 1:
      if ((dig2)||(dig1)){
      portb&=0x80;
      portb=portb|(0X7f & tabela[dig1]);
      #IFDEF CATODO_COMUM
      portb^=0x7f;
      #ENDIF
      porta=2;
      break;}
      case 2:
      if (dig2){
      portb&=0x80;
      portb=portb|(0X7f & tabela[dig2]);
      #IFDEF CATODO_COMUM
      portb^=0x7f;
      #ENDIF
      porta=4;
      break;}

      }

      }
      //==============================================================================
      // ROTINA DE INCREMENTO DA CONTAGEM
      //==============================================================================

      void incrementa() {
      //——————–altere aqui———————————-
      // rele=1;//comente aqui
      //if((dig0==9)&&(dig1==9)&&(dig2==9))return;//limita em ‘999’//de
      if((dig0==9)&&(dig1==9)&&(dig2==9)){ rele=1;return;}//limita em ‘999’//para
      //—————————————————————————-
      if(++dig0>9) {
      dig0=0;
      if(++dig1>9) {
      dig1=0;
      dig2++;
      }
      }

      }
      //==============================================================================
      // ROTINA DE DECREMENTO DA CONTAGEM
      //==============================================================================
      void decrementa() {

      //————————-altere aqui————————
      // if(!rele)return;//de
      if(!dig0 && !dig1 && !dig2)return; //para
      rele=0;//para
      //————————————————————-
      if(–dig0==0xff) {
      dig0=9;
      if(–dig1==0xff) {
      dig1=9;
      dig2–;
      }
      }
      //————–altere aqui——comente o abaixo ———————————–
      // if(!dig0 && !dig1 && !dig2){ rele=0;}//limita contagem em ‘000’
      //———————————————————————————–
      }

      //==============================================================================
      // ROTINA DE TESTE DE FALTA DE TENSÃO DA REDE ELÉTRICA
      //==============================================================================

      void testa_power(){
      if(!power){
      t0ie=0;//desliga interrupção
      porta=0;//salva energia dos capacitores da fonte desligando displays
      portb=0;
      write_eeprom(0,dig0); //salva valores do contador
      write_eeprom(1,dig1);
      write_eeprom(2,dig2);
      while(!power)delay_ms(50);//caso queda muito rápida para resetar o pic
      t0ie=1;//recupera após salvar
      //——————–altere aqui——————————————–
      // if(!dig2 && !dig1 && !dig0)rele=0; else rele=1;//retorna rele //de
      if((dig0==9) && (dig1==9) && (dig2==9)) rele=1; else rele=0; //para
      //—————————————————————————
      }
      }

      //==============================================================================
      // TESTA BOTÃO ‘UP’
      //==============================================================================
      void testa_up(){
      if(!sw_up) {
      for(a=0;a9)dig0=0;
      dig1=read_eeprom(1);
      if(dig1>9)dig1=0;
      dig2=read_eeprom(2);
      if(dig2>9)dig2=0;

      //——————–altere aqui———————————–
      // if(!dig0 && !dig1 && !dig2) rele=0; else rele=1;//de
      if((dig0==9) && (dig1==9) && (dig2==9)) rele=1; else rele=0; //para
      //liga rele somente com 999
      //————————————————————–
      t0ie=1;//habilita interrupções tmr0
      gie=1; //habilita interrupções geral

      //==============================================================================
      // LOOP PRINCIPAL
      //==============================================================================

      for(;;) {
      testa_reset();//testa botão de reset
      testa_up();//testa botão de incremento
      testa_down();//testa botão de decremento
      testa_power();//testa se não acabou energia

      }//while
      }//main

      1. Ola tudo certo fiz as alteraçãoes mais esta dando erro ao compilar (

        t0ie=1;//habilita interrupções tmr0 —– ele para bem aki e naõ gera o .hex
        gie=1; //habilita interrupções geral
        )

        ja resfiz varias vez e nada oque fiz de errado ?

        //******************************************************************************
        //
        // CONTADOR UP/DOWN/EEP C/RELE
        //
        // UTILIZA PIC 16F628A E 3 DISPLAY DE 7 SEGUIMENTOS
        //
        //
        // AUTOR : CLAUDIO LARIOS
        //
        // data: 21/01/2018
        //
        // Uso didático
        //
        // Blog PICSOURCE (LARIOS.TECNOLOGIA.WS)
        //
        //******************************************************************************

        #DEFINE CATODO_COMUM //descomente p/ catodo comum e comente p/ anodo comum
        //==============================================================================
        #include
        #fuses INTRC_IO,NOWDT,NOLVP,NOMCLR
        #use delay(clock=4000000) //tempo por instrução=1us
        #rom 0x2100 = {0,0,0}
        //bytes
        #byte porta = 0x05
        #byte portb = 0x06
        #byte trisa = 0x85
        #byte trisb = 0x86
        #bit gie = 0x0b.7
        #bit peie = 0x0b.6
        #bit t0ie = 0x0b.5
        //bits
        #bit zerar = 0x05.5 //interruptor ‘zerar’

        #bit sw_up = 0x05.6 //interruptor ‘up’
        #bit sw_down = 0x05.7 //interruptor ‘down’
        #bit power = 0x05.4 //sensor de falta de energia
        #bit rele = 0x06.7 //saída do rele

        //variavéis globais
        int dig2,dig1,dig0,mux,a;

        // Tabela para retorno dos seguimentos correspondentes aos números 0-9
        byte const tabela [] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

        #define VALOR_DBC 230 //valor para debounce das chaves

        //==============================================================================
        // ROTINA DE INTERRUPÇÃO DO TIMER 0
        //==============================================================================
        #INT_TIMER0
        //multiplexa display de 7 seg
        void display_refress() {
        if(++mux>2){ mux=0;}
        porta=0; //apaga seguimentos para mudança
        switch (mux) {
        case 0:
        portb&=0x80;
        portb=portb|(0X7f & tabela[dig0]);
        #IFDEF CATODO_COMUM
        portb^=0x7f;
        #ENDIF
        porta=1;
        break;
        case 1:
        if ((dig2)||(dig1)){
        portb&=0x80;
        portb=portb|(0X7f & tabela[dig1]);
        #IFDEF CATODO_COMUM
        portb^=0x7f;
        #ENDIF
        porta=2;
        break;}
        case 2:
        if (dig2){
        portb&=0x80;
        portb=portb|(0X7f & tabela[dig2]);
        #IFDEF CATODO_COMUM
        portb^=0x7f;
        #ENDIF
        porta=4;
        break;}

        }

        }
        //==============================================================================
        // ROTINA DE INCREMENTO DA CONTAGEM
        //==============================================================================

        void incrementa() {
        //rele=1;
        //if((dig0==9)&&(dig1==9)&&(dig2==9))return;//limita em ‘999’
        if((dig0==9)&&(dig1==9)&&(dig2==9)){ rele=1;return;}//limita em ‘999’//para
        if(++dig0>9) {
        dig0=0;
        if(++dig1>9) {
        dig1=0;
        dig2++;
        }
        }

        }
        //==============================================================================
        // ROTINA DE DECREMENTO DA CONTAGEM
        //==============================================================================
        void decrementa() {

        //if(!rele)return;
        if(!dig0 && !dig1 && !dig2)return; //para
        rele=0;//para
        //——————-
        if(–dig0==0xff) {
        dig0=9;
        if(–dig1==0xff) {
        dig1=9;
        dig2–;
        }
        }
        //————————
        //if(!dig0 && !dig1 && !dig2){ rele=0;}//limita contagem em ‘000’
        //————————

        }

        //==============================================================================
        // ROTINA DE TESTE DE FALTA DE TENSÃO DA REDE ELÉTRICA
        //==============================================================================

        void testa_power(){
        if(!power){
        t0ie=0;//desliga interrupção
        porta=0;//salva energia dos capacitores da fonte desligando displays
        portb=0;
        write_eeprom(0,dig0); //salva valores do contador
        write_eeprom(1,dig1);
        write_eeprom(2,dig2);
        while(!power)delay_ms(50);//caso queda muito rápida para resetar o pic
        t0ie=1;//recupera após salvar
        //if(!dig2 && !dig1 && !dig0)rele=0; else rele=1;//retorna rele
        if((dig0==9) && (dig1==9) && (dig2==9)) rele=1; else rele=0; //para
        }
        }

        //==============================================================================
        // TESTA BOTÃO ‘UP’
        //==============================================================================
        void testa_up(){
        if(!sw_up) {
        for(a=0;a<VALOR_DBC;A++){//debounce para botão apertado
        testa_power();
        if(sw_up) return;
        }
        incrementa();
        for(a=0;a<VALOR_DBC;A++){//debunce ao soltar o botão
        testa_power();
        if(!sw_up)a=0;
        }

        }//inc
        }
        //==============================================================================
        // TESTA BOTÃO 'DOWN'
        //==============================================================================

        void testa_down(){
        if(!sw_down) {
        for(a=0;a<VALOR_DBC;A++){//debounce para botão apertado
        testa_power();
        if(sw_down) return;
        }
        decrementa();
        for(a=0;a9)dig0=0;
        dig1=read_eeprom(1);
        if(dig1>9)dig1=0;
        dig2=read_eeprom(2);
        if(dig2>9)dig2=0;
        //if(!dig0 && !dig1 && !dig2) rele=0; else rele=1;
        if((dig0==9) && (dig1==9) && (dig2==9)) rele=1; else rele=0; //para

        t0ie=1;//habilita interrupções tmr0
        gie=1; //habilita interrupções geral

        //==============================================================================
        // LOOP PRINCIPAL
        //==============================================================================

        for(;;) {
        testa_reset();//testa botão de reset
        testa_up();//testa botão de incremento
        testa_down();//testa botão de decremento
        testa_power();//testa se não acabou energia

        }//while
        }//main

          1. Opa boa noite

            aparece para min assim

            ***Error 76 ” codigo.c” line 228(4,8): Expect ;
            ***Error 51 ”codigo.c” line 236(10,11): A numeric expression must appear here
            ***Error 51 ” Codigo.c” line 236(11,12): A numeric expression must appear here

            Saberia dizer oque pode ser esse erro ?

          2. Olá Julio!
            O erro 76 indica que foi esquecido de colocar ‘;’ (ponto traço) no final de alguma instrução. Clique no código de erro que uma seta mostrará a região do problema. Pode estar acima, na mesma linha ou abaixo da seta. Olhe cuidadosamente a procura do lugar onde foi esquecido o ‘;’.

          3. Ola boa tarde funcionou 100% achei o erro e só por o ” ; ” no final da linha 225 que funciona 100%.

            Valeu Claudio.

            Assim que sobrar uma grana faço uma doação pro site esta sendo de grande ajuda o teu conteúdo

  3. HOLA CLAUDIO
    el contador cuando cuenta hacia bajo el rele se atiba en cero vien. pero si y quri programar de 0a40
    es decir haciarriba cuando se atiba el rele

    luego te quiro hacer una pregunta sino te es mucha molestia en el porton REF012
    cuando boton del mando pulsado va dando impulsos el motor es decir no se queda la retencin fijada
    pulsando el manado unos segundos

    Muchas gracias y un gran Saludo Manuel Pereira

    1. Olá Manuel!
      Este contador no es un temporizador. Sólo muestra el valor puesto por el usuario a través de los botones up y down.
      No tiene opción para cambiar esto en el código. Tendria que ser rehecho para operar como desea.
      Cuanto a ref12 en el futuro, quien sabe, revisar este artículo.

  4. coloquei o transformador de 12 volts, ficou tudo como ta no esquema,muito bom, mas no contar e decontar, ce aperta umas 10, uns 2 ou 3 aperto nao conta…

    1. Olá Raimundo!
      Devido ao ‘debounce’ dos interruptores (eliminação dos ruídos do contatos ao abrir/fechar) serem um pouco lento, poderá acontecer de falhar a contagem se for acionado rápido. Tente a nova versão, com um ‘debounce’ melhorado, que acredito possa resolver o problema. Pasta zipada CONTADOR_UP_DOWN_EEP_V2.

Comments are closed.

Back To Top