Construindo um Capacímetro Digital com um Arduino

Este projeto foi inspirado em um kit de capacímetro da Sparkfun (http://www.sparkfun.com/products/9485). Na época que eu montei, não estava disponível o fonte do firmware. Isto me levou a criar uma versão minha (usando hardware próprio ao invés do Arduino), e uma oficina de montagem em protoboard. Invertendo o processo usual, adaptei o projeto para uso com o Arduino.

 

 

Objetivos

 

O primeiro objetivo deste projeto é ilustrar a versatilidade dos microcontroladores, mostrando como os seus recursos podem ser usados para construir um capacímetro digital.

 

Não menos importante é  passar alguns conceitos básicos de eletrônica e microcontroladores.

 

Eletrônica Básica

 

Como ponto de partida vamos ver a tensão elétrica. A tensão é um potencial. Assim como podemos falar no potencial gravitacional de um objeto suspenso a uma certa altura, é uma característica que indica a possibilidade de algo acontecer. Ao soltar um objeto, o potencial gera movimento. Ao fecharmos um circuito elétrico, a tensão gera a corrente elétrica.

 

A forma mais simples de se fechar um circuito elétrico é com uma resistência. Neste caso a lei de Ohm define a relação entre tensão, resistência e corrente:

 

O capacitor é um componente mais complexo. Ele é construído por duas placas condutoras separadas por um isolante, o que sugere que não existirá corrente elétrica fluindo por ele. Entretanto, ao conectar um capacitor a uma fonte de tensão, temos uma corrente à medida que elétrons se movem para deixar as placas do capacitor na mesma tensão que a fonte. Esta corrente é inicialmente alta e diminui à medida que a tensão no capacitor aumenta (dizemos que o capacitor estará sendo carregado). Paradoxalmente, um capacitor descarregado se comporta inicialmente como um curto quanto ligado a uma fonte. Se a fonte for retirada, o capacitor tende a manter a tensão obtida (os capacitores reais irão perder aos poucos esta carga). Se no lugar da fonte colocarmos um curto, a tensão existente no capacitor irá gerar uma corrente elétrica alta, que irá diminuindo à medida que o capacitor descarregar. 

 

O comportamento de carga e descarga dos capacitores é determinado por uma fórmula matemática:

Um outro componente que vamos usar no nosso projeto é o LED – diodo emissor de luz. Antes de mais nada, o LED é diodo, o que significa que se comporta como uma mão única de corrente. Se a tensão é ligada em um sentido, a corrente flui. No sentido inverso, não flui. Na direção correta, a tensão positiva é conectada ao anodo e a negativa ao catodo.  A intensidade do LED é determinada pela corrente que passa por ele.

A tensão sobre o LED varia pouco com a corrente. Em uma montagem típica (como a acima) a corrente que passa no LED é determinada por um resistor: I = (V-Vled)/R. Normalmente escolhemos a corrente que nos fornece a luminosidade desejada e calculamos o valor de R para obtê-la.

 

Neste projeto vamos usar um display LED de 7 segmentos. Este display possui sete LEDs (oito, contando o ponto decimal), possibilitando apresentar um dígito numérico (e certas letras, com um pouco de boa vontade de quem lê). 

 

Vamos precisar  de quatro dígitos, o que gera um problema de montagem. Se tivermos os dois terminais de de cada LED, teremos um total de 4 x 8 x 2 = 64 conexões. Uma primeira simplificação é conectar internamente ao display todos os terminais de um lado dos LEDs, criando uma configuração de anodo (ou catodo) comum. Ainda assim, teríamos 4 x 9 = 39 conexões. Para reduzir mais, usamos a ideia da multiplexação: conectamos juntos os segmentos dos dígitos e, usando o terminal comum, acendemos os dígitos um a um. Fazendo este processo rapidamente, a vista tem a impressão que todos os dígitos estão acesos simultaneamente.

 

Microcontroladores

 

Um microcontrolador é um computador em um chip: contém a CPU, memória e periféricos integrados. O seu uso típico é colocar inteligência em produtos.

 

Em um microcontrolador é comum termos três tipos de memória:

  • Flash: memória não volátil (mantém conteúdo quando desligada), usada para o programa. A escrita na Flash requer procedimentos especiais e demorados.
  • Ram: memória volátil (perde o conteúdo quando desligada), usada para as variáveis do programa
  • EEProm: memória não volátil mas com escrita mais simples e rápida que a Flash, usada para configurações e dados que precisam ser mantidos quando o microcontrolador for desligado.

 

O Arduino Nano usa o microcontrolador ATmega328.

 

Um periférico que existe em praticamente todo microcontrolador é a Entrada e Saída Digital

  • Entrada digital: tensão no pino é lida como 0 ou 1
  • Saída digital: tensão no pino é controlada pelo software entre os níveis “alto” e “baixo”

 

O ATmega328 tem 20 pinos de E/S digital (os pinos do Arduino marcados como Entradas Analógicas podem ser usados também par E/S digital).

 

Como todo computador, os microcontroladores possuem um clock, que é a base de tempo p/ execução do programa. Este clock é também usado pelos timers – contadores que podem:

  • Interromper periodicamente o programa
  • Gerar formas de onda (PWM)
  • Medir tempo entre eventos

 

O ATmega328 possui 3 timers:

  • Timers 0 e 2 de 8 bits (contam de 0 a 255)
  • Timer 1 de 16 bits (contam de 0 a 65535), com suporte a medição de tempo entre eventos

 

As bibliotecas do Arduino usam o Timer 0 para as funções delay() e milis(). O timer2 é usado apenas da função tone(). Os três timers podem ser usados na geração de PWM (função analogWrite()). No meu projeto vou usar os timers 1 e 2; para isto não vou utilizar PWM nem tone().

 

Um último recurso necessário para o projeto do capacímetro é a observação de uma tensão analógica. Quando falamos disso, a primeira coisa que vem em mente é o ADC – conversor analógico digital – usado no Arduino através do analogRead(). O ADC fornece um número proporcional à tensão no pino, algo extremamente útil em muitas aplicações. Entretanto, o ADC requer um certo tempo para fazer a conversão e o seu resultado precisa ser analisado pelo programa.

 

Um recurso mais simples é o comparador analógico, que fornece uma saída em 0 ou 1 conforme a tensão em um pino for maior ou menor que uma referência interna ou externa. Melhor ainda, a saída do comparador do ATmega328 pode ser conectada internamente ao Timer1 para encerrar uma contagem de tempo.

 

Projeto do Capacímetro

 

A ideia básica é medir o tempo necessário para o capacitor carregar até uma certa tensão. Conhecidas as tensões e a resistência em série com o capacitor, podemos usar a fórmula que vimos antes para determinar o valor do capacitor.

 

Uma saída digital será usada para descarregar o capacitor e liberar a sua carga. A tensão do capacitor será monitorada pelo comparador, que para o Timer1 quando ela atingir a tensão desejada. Desta forma o tempo de carga será medido diretamente pelo hardware, sem necessidade de controle direto pelo software.

 

O Timer2 será utilizado para controlar a multiplexação do display e para dar um tempo entre as medições.

 

O Arduino trabalha com um clock de 16MHz, tanto o Timer1 como o Timer2 podem trabalhar com uma fração deste clock. Os seguintes valores serão usados:

  • Timer2
    • Usa clock/64, conta de 0 até 255
    • Tempo até “dar a volta” (overflow) = 256*64/16000000 = 1,024ms
    • Para obter uma medição a cada 2 segundos:  contar 1953 interrupções
  • Timer1
    • Usa clock/64, conta de 0 até 65535
    • Permite medir de 4 µs a 262 ms
  • Cálculo do Capacitor
    • Referência = 2,5V, R = 330K
    • T µs = 0,2287*C pF ou C = (contagem*40000)/2287 pF
    • Permite medir C de 17pF a 1.15uF

 

Projeto do Hardware

 

As figuras a seguir se referem ao hardware próprio, com anotações sobre o uso do Arduino.

 

Alimentação:

 

Regulador para baixar tensão de bateria de 9V para os 5V usados no circuito. Arduino possui circuito semelhante:

 

 

 

Alimentação, reset e clock do microcontrolador

Estes circuitos estão na placa do Arduino

 

 

Ligação do Capacitor e Referência de Tensão:

 

No circuito abaixo os resistores de 47K e 330K são resistores de 1% de precisão.

 

Quando o pino 12 estiver em nível baixo, o capacitor será descarregado através do resistor de 120R. Para carregar o capacitor o pino será configurado como entrada, passando a apenas monitorar a tensão.

 

 

Ligação do display

 

No Arduino os sinais DIG1 e DIG2 são ligados às saídas digitais 4 e 5 para não interferir com a comunicação serial do bootloader

 

Montagem em Protoboard usando o Arduino Nano

 

 

Listagem do Software

 

#define FALSE   0
#define TRUE    1

// Variaveis
static volatile char fAmostra;          // TRUE quando é hora de amostrar
static volatile char fAmostrado;        // TRUE quando é amostra foi feita
static volatile unsigned int tempo;     // tempo para carga do capacitor até 2,5V
                                        // zero indica que ultrapassou o tempo máximo

// Um byte para cada segmento
// Bit 7 = segto G, Bit 6 = segto F, .. Bit 1 = segto A, Bit 0 = ponto
static unsigned char segto    [4];
static unsigned char newsegto [4];

// Controle dos segmentos do display
// Segmentos a serem acesos para cada dígito
// 0 1 2 3 4 5 6 7 8 9 - P n u
static const unsigned char segDigito[14] =
{
  0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,
  0b11011010, 0b11111010, 0b00001110, 0b11111110, 0b11011110,
  0b10000000, 0b11100110, 0b10101000, 0b00111000
};

#define TRACO   10
#define C_PICO  11
#define C_NANO  12
#define C_MICRO 13


// Iniciação
void setup() {
    // Pinos ligados ao display são de saída
    pinMode (A0, OUTPUT);  digitalWrite(A0, LOW);
    pinMode (A1, OUTPUT);  digitalWrite(A1, LOW);
    pinMode (A2, OUTPUT);  digitalWrite(A2, LOW);
    pinMode (A3, OUTPUT);  digitalWrite(A3, LOW);
    pinMode (A4, OUTPUT);  digitalWrite(A4, LOW);
    pinMode (A5, OUTPUT);  digitalWrite(A5, LOW);
    pinMode (9, OUTPUT);   digitalWrite(9, LOW);
    pinMode (10, OUTPUT);  digitalWrite(10, LOW);
    
    // Pinos de seleção do dígito (catodos) em 1
    pinMode (2, OUTPUT);   digitalWrite(2, HIGH);
    pinMode (3, OUTPUT);   digitalWrite(3, HIGH);
    pinMode (4, OUTPUT);   digitalWrite(4, HIGH);
    pinMode (5, OUTPUT);   digitalWrite(5, HIGH);

    // Configura o comparador
    ADCSRB &= ~_BV(ACME);
    ACSR = _BV(ACIC) | _BV(ACIS1) | _BV(ACIS0); 
    DIDR1 = _BV(AIN1D) | _BV(AIN0D);

    // Configura o timer 2
    TCCR2A = 0;                         // Modo normal
    TCCR2B = _BV(CS22);                 // Usar clkIO/64
    TIMSK2 = _BV(TOIE2);                // Interromper no overflow
    
    // Configura o timer 1
    TCCR1A = 0;                     // Operação normal
    TCCR1B = 0;                     // Contador parado
    TCCR1C = 0;                     // Não usado
    
    // Descarrega o capacitor
    DescarregaCap ();
    
    // LED de debug
    pinMode (LED_BUILTIN, OUTPUT);
}

void loop() {
    if (fAmostra)                   // Quando for hora de amostrar
    {
        digitalWrite (LED_BUILTIN, HIGH);
        Amostra ();                     // Amostra
        fAmostra = FALSE;
        CalculaCap ();                  // Calcula
        AtlDisp ();                     // Mostra no display
        digitalWrite (LED_BUILTIN, LOW);
    }
}

// Calcula o valor do capacitor e coloca em newsegto
static void CalculaCap (void)
{
    if (tempo < 3)
    {
        // Ccapacitância muito alta ou ausente
        newsegto[0] = newsegto[1] = newsegto[2] = newsegto[3] = segDigito[TRACO];
    }
    else
    {
        // Capacitor está entre 17pF e 1.146.218 pF (1.15uF)
        unsigned long cap = (tempo * 40000UL) / 2287UL;     // em pF
        unsigned int valor;
        unsigned int ponto;
        if (cap < 1000UL)
        {
            valor = (unsigned int) cap;
            newsegto [3] = segDigito[C_PICO];
            ponto = 2;
        }
        else if (cap < 9995UL)
        {
            cap += 5UL;       // arredonda
            valor = ((unsigned int) cap) / 10;
            newsegto [3] = segDigito[C_NANO];
            ponto = 0;
        }
        else if (cap < 99950UL)
        {
            cap += 50UL;       // arredonda
            valor = (unsigned int) (cap / 100UL);
            newsegto [3] = segDigito[C_NANO];
            ponto = 1;
        }
        else if (cap < 999500UL)
        {
            cap += 500UL;       // arredonda
            valor = (unsigned int) (cap / 1000UL);
            newsegto [3] = segDigito[C_NANO];
            ponto = 2;
        }
        else
        {
            cap += 5000UL;       // arredonda
            valor = (unsigned int) (cap / 10000UL);
            newsegto [3] = segDigito[C_MICRO];
            ponto = 0;
        }
        newsegto [0] = segDigito [valor / 100];
        newsegto [1] = segDigito [(valor / 10) % 10];
        newsegto [2] = segDigito [valor % 10];
        newsegto [ponto] |= 1;  // coloca o ponto
    }
}

// Tratamento da interrupção do timer 0, usada para varrer o display
// e disparar a amostragem
#define DLY_AMOSTRA 1953        // 1 amostra a cada 2 segundos
ISR (TIMER2_OVF_vect)
{
    static unsigned int cntAmostra = DLY_AMOSTRA;
    
    VarreDisp ();
    if (--cntAmostra == 0)
    {
        cntAmostra = DLY_AMOSTRA;
        fAmostra = TRUE;
    }
}

// Amostragem do valor do capacitor
static void Amostra (void)
{
    fAmostrado = FALSE;

    TCNT1H = 0;
    TCNT1L = 0;
    
    CarregaCap ();
    
    TIMSK1 = _BV(ICIE1) | _BV(TOIE1);
    TCCR1B = _BV(ICES1) | _BV(CS11) | _BV(CS10);
    
    while (!fAmostrado)
        ;
    
    DescarregaCap ();
}

// Atualiza o display para mostrar o conteudo de newsegto
static void AtlDisp (void)
{
   segto[0] = newsegto[0];
   segto[1] = newsegto[1];
   segto[2] = newsegto[2];
   segto[3] = newsegto[3];
}   

// Faz a varredura (apresentação do display)
// Esta rotina está amarrada à conexão do display ao ATmega
static void VarreDisp (void)
{
    static unsigned char disp = 0;
    register unsigned char segtos;

    // apaga o digito anterior
    if (disp == 0)
        digitalWrite(3, HIGH);
    else if (disp == 1)
        digitalWrite(4, HIGH);
    else if (disp == 2)
        digitalWrite(5, HIGH);
    else
        digitalWrite(2, HIGH);
       
    // programa os segmentos
    segtos = segto[disp];
    if (segtos & 0x80)      // G - PB2
        PORTB |= 0x04;
    else
        PORTB &= ~0x04;
    if (segtos & 0x40)      // F - PC0
        PORTC |= 0x01;
    else
        PORTC &= ~0x01;
    if (segtos & 0x20)      // E - PC1
        PORTC |= 0x02;
    else
        PORTC &= ~0x02;
    if (segtos & 0x10)      // D - PC2
        PORTC |= 0x04;
    else
        PORTC &= ~0x04;
    if (segtos & 0x08)      // C - PC3
        PORTC |= 0x08;
    else
        PORTC &= ~0x08;
    if (segtos & 0x04)      // B - PC4
        PORTC |= 0x10;
    else
        PORTC &= ~0x10;
    if (segtos & 0x02)      // A - PC5
        PORTC |= 0x20;
    else
        PORTC &= ~0x20;
    if (segtos & 0x01)      // PONTO - PB1
        PORTB |= 0x02;
    else
        PORTB &= ~0x02;
    
    // acende o dígito atual
    if (disp == 0)
        digitalWrite(4, LOW);
    else if (disp == 1)
        digitalWrite(5, LOW);
    else if (disp == 2)
        digitalWrite(2, LOW);
    else
        digitalWrite(3, LOW);
    
   // passa para o dígito seguinte
   disp = (disp + 1) & 3;
}

// Descarrega o capacitor
static void DescarregaCap (void)
{
    // Configura pino AIN0/D6 como saída digital
    // em nível 0
    DDRD |= _BV(PD6);
    PORTD &= ~_BV(PD6);
    pinMode (6, OUTPUT);
    digitalWrite (6, LOW);
}

// Dispara a carga do capacitor
static void CarregaCap (void)
{
    // Configura o pino AIN0/D6 como entrada digital
    // (sem pullup) para não interferir
    pinMode (6, INPUT);
    digitalWrite (6, LOW);
}

// Tratamento da interrupção de captura do timer1, 
ISR (TIMER1_CAPT_vect)
{
    unsigned char cntLo, cntHi;
    cntLo = ICR1L;                  // ler primeiro Low
    cntHi = ICR1H;
    tempo = (cntHi << 8) + cntLo;
    TCCR1B = 0;                     // para o timer
    TIMSK1 = 0;                     // não interromper
    fAmostrado = TRUE;
}

// Tratamento da interrupção de overflow do timer1
ISR (TIMER1_OVF_vect)
{
    tempo = 0;                      // indica overflow
    TCCR1B = 0;                     // para o timer
    TIMSK1 = 0;                     // não interromper
    fAmostrado = TRUE;
}

 

O que achou do projeto? Deixe seu comentário abaixo.

 

Saiba Mais

 

Introdução ao Arduino - Primeiros passos na plataforma

Construindo sua placa Arduino

Medindo distância com Arduino UNO utilizando sensor ultrassônico e LCD

Medindo temperatura e umidade com Arduino UNO utilizando Si7021 e LCD

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

4
Deixe um comentário

avatar
 
4 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
4 Comment authors
Caiubi StaffokerMarcos Roberto Ruybal BicaIsmaelFernando Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Caiubi Staffoker
Membro
Caiubi Staffoker

Projeto bem interessante! Eu cheguei a fazer um na faculdade sobre leitor de resistor.
Apenas um adendo, na parte que você começa assim: "Vamos precisar de quatro dígitos...", acredito que teve uma infelicidade de deixar 4x9 = 39 e na verdade o resultado desta multiplicação é 36.

Marcos Roberto Ruybal Bica
Visitante
Marcos Bica

Artigo muito bom, tem uma abordagem didática, e apresenta o microcontrolador na plataforma. O código explora as funcionalidades do arduíno, mas sem ficar restrito às bibliotecas prontas. Parabéns. Muito bom. Vou indicar a leitura para os meus alunos.

Ismael
Visitante
Ismael

Muito bom!
Obrigado por compartilhar seu conhecimento.
Como não entendo quase nada de eletrônica, oq mais gostei foi a parte de eletrônica básica! 😉

Fernando
Visitante
Fernando

Muito legal o projeto é muito bem explicado! Obrigado pelo projeto!