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

5

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

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!

5

40 comments

  1. 0

    Boa tarde. utilizo numa antena rastreável. um contador de voltas digital Up down da FUNKITS. está com problemas. resolvi comprar um na China GDD5135A-CD200V/50A-P12V. aceitável, mas está se perdendo nos registos no sentido que uso um relê, pois os pulsos saltam. Não sou tec. preciso de uma solução para este caso. ou se alguém puder fazer um com 3 dígitos, me informe o custo. possivelmente, utilizarei 3 unidades.
    Obs. O reset a qualquer momento da contagem, guardando a última posição ao desligar. utilizo um sensor óptico para esta contagem.
    atenciosamente,
    orlando jr

    1. 0

      Olá Orlando Jr!
      Tente colocar esta sua proposta no fórum. Pode ser que alguém se interesse em produzir estes contadores ou indicar uma solução para o que você já tem, como por exemplo, colocar um diodo na bobina do rele para não gerar pulsos de alta tensão quando ele desliga.

  2. 0

    O código mais próximo que achei até agora é esse, mas está em outra linguagem, precisa ser para o pic16f877a e compilador CCS:

    void display_char (char character){
    //Uint8 i;
    //Uint8 index;
    //index=((int)character – 32)*5;
    //if ((int)character >=32 && (int)character <=127){
    //for (i=0;i< 5; i++){
    //OSD9616_send(0x40,font[(index+4)-i]);
    //}
    //OSD9616_send(0x40,0x00); // space between characters
    //}
    //}

  3. 0

    Olá Claudio Larios, utilizei o código fonte de quatro displays anodo comum, adicionei mais um display ficando com cinco e converti o código para PIC 16F877A. Funcionou perfeitamente, gostei muito do desempenho.
    Agora solicito ajuda para o código que faça a seguinte função: Ao clicar em “up” deve aparecer “5”, ao clicar novamente apareça “10”, depois “15”, depois “20” e assim por diante (multiplica 1 pulso por 5). Isso é só no display, na saída i2c deve sair pulso a pulso sem a multiplicação. Isso é importante, se preferir entrar em contato por e-mail, ficarei grato: euclidesmz@gmail.com
    Desde já, obrigado.

  4. 2

    conheci o site a pouco tempo e ja posso dizer sem duvida q e um dos melhores da web!! vc tem ai um projeto de um contador de pulsos programavel? tipo quero que conte ate o numero que eu escolher e depois ative o rele para desligar o motor. pode ser com displays lcd. por favor eu preciso muito para usar na minha rebobinadora de motor. aguardo resposta e obrigado desde ja!

  5. 0

    Olá, boa noite!,
    fiz alteraçao no 999 rele liga, pra 600 rele liga, nao entendo de programa, so alterei os numeros no lugar dos dig 0, dig1 e dig 2, limita em 600, compilei no ccs compiler 5.0, deu certo, só que funciona apagado, ce coloca o numero (1), aparece (E), que será? pode me dizer alguma coisa, compilador ?!!, bios !!????

    1. 0

      Olá Raimundo!
      Você deve estar usando provavelmente o display cátodo comum. Mas o programa está para ânodo comum. Altere então a seguinte linha, por simplesmente descomentá-la ( ou seja, apagar os // da frente):

      de:
      // #DEFINE CATODO_COMUM //descomente p/ catodo comum e comente p/ anodo comum

      para:

      #DEFINE CATODO_COMUM //descomente p/ catodo comum e comente p/ anodo comum

      Depois recompile para obter o hex correto.

  6. 0

    nóis de novo, o up down com dois digitos versao 2, acabei de montar, ele conta com os segmentos apagados, acende o contrario, por exemplo o numero 1, o display mostra uma letra E ou um 3 ao contrario, ja inverti o hex catodo pra anodo, to usando display catodo, da uma luz ai pra nois se possivel, agradeço sua paciencia !!

  7. 0

    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!!!

  8. 0

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

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

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

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

            vou da uma olhada e digo se funcionou e ja aviso vi que o amigo Raimundo também tem interesse nesse de ligar com 999.

          4. 0

            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

  9. 0

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

      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.

    1. 0

      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.

  10. 0

    Oi ?!!, relacionado ao contador digital up down, as vezes tem falha(nao responde) tanto no incremento quanto no decremento, parece que ta muito sensivel, tem como a resposta ficar um pouco mais lenta, se tiver jeito de alterar, manda o hex pra nois, grato!!

      1. 0

        bom, montei o circuito seguindo o esquema, usei botao de pressao, a fonte, usei uma de 12v-2A(sem trafo) daquelas de monitor lcd,… ex: vou contar ate 10, tres apertao nao responde, nao e em sequencia,… tanto com botao ou clicando os fios e ou foto-acoplador, acontece, to usando so o circuito por enquanto !!

      2. 0

        sabe quando ce aperta o botao de contar e ele da umas atropeladas, outra ora falha pra marcar, nao sei como funciona dentro do pic, se voce poe um tempo, mas parece que e algo com milisegundos de aceitaçao de comando e ou resposta do aperto do botao, ta sensivel, aqueles resistores de 10k, la nos botao, sera que tem algo a ver, porque no contador de voltas usa valor menor, ou e no programa, !!!

Leave a Reply